-
Notifications
You must be signed in to change notification settings - Fork 11
8. ServerMiddleware vs Middleware
The middleware allows you to define custom functions that can be executed before rendering a page or a group of pages.
Each middleware must be placed in the middleware / directory. The name of the file will be the name of the middleware(middleware/i18n.js
).
We will use this concept to add the internalization of our site using vue-i18n.
- Run in terminal:
npm i -S vue-i18n
- We create a middleware called
i18n.js
with the following content that will be executed before rendering any page
export default function ({ app, store, params, error }) {
// Get locale from params
const locale = params.lang || 'en'
if (store.state.locales.indexOf(locale) === -1) {
return error({ message: 'This page could not be found.', statusCode: 404 })
}
// Set locale
store.commit('SET_LANG', locale)
app.i18n.locale = store.state.locale
}
- We add a plugin
i18n.js
and register it innuxt.config.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
export default ({ app, store }) => {
// Set i18n instance on app
// This way we can use it in middleware and pages asyncData/fetch
app.i18n = new VueI18n({
locale: store.state.locale,
fallbackLocale: 'en',
messages: {
'en': require('~/locales/en.json'),
'es': require('~/locales/es.json')
}
})
}
- We create the
json
files that will have our translation in thelocal
folder for both the English languageen.json
and Spanishes.json
:
{
"LINKS": {
"HOME": "Home",
"POST": "Post"
},
"HOME": {
"TITLE": "Home",
"INTRODUCTION": "Welcome to Gixt Blog!!",
"BY": "By",
"COMMENTS": "Comments",
"READ_MORE": "Read More",
"SEARCH": "Search"
},
"POST": {
"TITLE": "Post",
"AUTHOR": "Author",
"COMMENTS": "Comments"
}
}
- And finally in our
store
we add the necessary elements for the correct functioning of the internalization
const createStore = () => {
return new Vuex.Store({
state: {
gists: [],
// add
locales: ['en', 'es'],
locale: 'en'
},
...
mutations: {
...
// add
SET_LANG (state, locale) {
if (state.locales.indexOf(locale) !== -1) {
state.locale = locale
}
},
...
}
- Now it's just changing the texts present in the
GistArticle.vue
component by thet()
function ofi18n
<template>
<article class="post post-large">
<div v-if="gist">
<div class="post-date">
<span class="day">{{ gist.created_at | date('D') }}</span>
<span class="month">{{ gist.created_at | date('MMM') }}</span>
</div>
<div class="post-content">
<h2>
<nuxt-link :to="{ path: '/post/'+ gist.id }">{{gist.description}}</nuxt-link>
</h2>
<!-- Content Raw -->
<p>{{gist.description}}</p>
<!-- /Content Raw -->
<div class="post-meta">
<span>
<i class="fa fa-user"></i>
{{ $t('HOME.BY') }} <a :href="gist.owner.html_url" target="_blank">{{ gist.owner.login }}</a>
</span>
<span>
<i class="fa fa-comments"></i>
<a :href="gist.comments_url" target="_blank">{{ gist.comments }} {{ $t('HOME.COMMENTS') }}</a>
</span>
<a :href="'/post/'+ gist.id +''" class="btn btn-xs btn-primary pull-right">{{ $t('HOME.READ_MORE') }}...</a>
</div>
</div>
</div>
</article>
</template>
Are just running in server side before vue-server-renderer and can be used for server specific tasks like handling API requests or serving assets.
To better demonstrate this property, let's use the post/create
page by creating a simple API in Express.js
that allows us to create a Gist directly on GitHub.
-
serverMiddleware in file
nuxt.config.js
const bodyParser = require('body-parser')
module.exports = {
...
// Agregar
serverMiddleware: [
// body-parser middleware
bodyParser.json(),
// Api middleware
'~/api'
],
}
We are going to have this file tree in our api folder (in the root of the project):
api/
--| gist.js
--| index.js
- Install the necessary dependencies for our api with express.js
$ npm i -S body-parser express request-promise
gist.js
const { Router } = require('express')
const request = require('request-promise')
const router = Router()
// Personal access tokens: https://github.com/settings/tokens
const TOKEN_GITHUB = '3ab7b782c21a3b3f329129654494dc5ca79e977c'
function create (req, res, next) {
const gist = {
description: req.body.description,
public: true,
files: req.body.files
}
const options = {
url: 'https://api.github.com/gists',
method: 'POST',
headers: {
'Authorization': `token ${TOKEN_GITHUB}`,
'User-Agent': 'khriztianmoreno'
},
json: true,
body: gist
}
return request(options)
.then(response => res.status(200).json(response))
.catch(error => res.status(500).json(error))
}
router.post('/gist/', create)
module.exports = router
It is necessary to create a GitHub personal access tokens in order to create the gist associated with a user, otherwise it would be created anonymously GitHub personal access tokens
index.js
const express = require('express')
const gist = require('./gist')
// Create express router
const router = express.Router()
// Transform req & res to have the same API as express
// So we can use res.status() & res.json()
const app = express()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
// Add Gist Routes
router.use(gist)
// Export the server middleware
module.exports = {
path: '/api',
handler: router
}
To achieve an appearance of editor markdown in our form we are going to install these libraries
$ npm i -S marked lodash
- Finally our creation form of a new Gist
pages/post/create.vue
would look like this:
<template>
<div>
<form name="form" @submit.prevent="onSubmit()">
<div class="form-group">
<label for="name">Title</label>
<input type="text" v-model.trim="title" class="form-control" id="name" placeholder="Enter title" required>
</div>
<div class="form-group">
<label for="description">Description</label>
<div class="row">
<div class="col-sm-6">
<textarea :value="input" @input="update" name="description" rows="10">
</textarea>
</div>
<div class="col-sm-6">
<div id="editor-result" v-html="compiledMarkdown"></div>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary" >Guardar</button>
</form>
</div>
</template>
<script>
import axios from 'axios'
import marked from 'marked'
import _ from 'lodash'
export default {
data () {
return {
input: '# hello',
title: ''
}
},
computed: {
compiledMarkdown: function () {
return marked(this.input, { sanitize: true })
}
},
methods: {
update: _.debounce(function (e) {
this.input = e.target.value
}, 300),
async onSubmit () {
const file = `${this.title.toLowerCase().replace(/\s/g, '-')}.md`
const gist = {
description: this.title,
files: {}
}
gist.files[file] = { content: this.input }
try {
await axios.post('/api/gist', gist)
alert('Yeah!!')
} catch (error) {
console.log('Error', error)
}
}
}
}
</script>
<style lang="scss" scoped>
#editor {
margin: 0;
height: 100%;
font-family: "Helvetica Neue", Arial, sans-serif;
color: #333;
&-result {
border: 1px solid;
height: 100%;
padding: 10px;
}
}
textarea,
#editor div {
display: inline-block;
width: 100%;
height: 100%;
vertical-align: top;
box-sizing: border-box;
padding: 0 20px;
}
textarea {
border: none;
border-right: 1px solid #ccc;
resize: none;
outline: none;
background-color: #f6f6f6;
font-size: 14px;
font-family: "Monaco", courier, monospace;
padding: 20px;
}
code {
color: #f66;
}
</style>
To see these steps complete, you can change to the 8-serverMiddleware-middleware
branch in this repository.
Hello, my name is Cristian Moreno.
I'm a community leader and altruistic speaker, JavaScript/Node.js evangelist and FullStack Javascript Developer. Currently co-organize Medellin.js (Biggest JavaScript user group in Colombia), Avanet and Azure Cloud Medellin communities.
I love developing things, especially ideas, giving them a new perspective and making them shine! products and applications come after I transform ideas into code; I'm passionate about software development and related stuff.