Tạo middleware cho web với Next.js

Lời mở đầu

Nếu như Nuxt.js là một framework giúp chúng ta xây dựng trang web với Vuejs sử dụng cơ chế Server Side Rendering (Dữ liệu render từ phía server. Dùng cách này thì khi googlebot crawl đến link nào từ trang web sẽ đọc được content từ trang web đầy đủ). Thì khi chúng ta xây dựng trang web với Reactjs có một framework tương ứng mà mọi người có thể biết đó là Next.js.

Nội dung chính

1. Giới thiệu về Next.js

Universal Javascript App là gì?

Universal Javascript App là ứng dụng web được viết bằng javascript (trước đây còn được gọi là isomorphic), chạy được cả server và phía browser chung một mã nguồn. Tất nhiên có một số chỉ cần chạy phía browser như các hiệu ứng chuyển trang, chuyển động, hiệu ứng của các phần tử, hoặc có sử dụng local storage của browser, còn một số mã nguồn liên quan đến lớp xử lý (business logic) hoặc các hoạt động đặc thù của server như phiên làm việc (session), xác thực (authentication),…

Giới thiệu về Nextjs

Next.js là một framework nhỏ gọn được xây dựng từ React, Babel và Wepack được tạo ra để giúp lập trình viên tạo ứng dụng web React có tính năng SSR (Server Side Render). Như các bạn đã biết với React.js, thì chúng ta đang xây dựng ứng dụng front-end với UI được quản lý bởi React, điểm yếu là chỉ client-side-render, nên việc SEO sẽ gặp khó khăn (có thể khắc phục bằng hệ thống ở server có khả năng xử lý javascript như Prerender.io), Next.js giúp chúng ta có tính năng SSR rất dễ dàng.

2. Bài toán

Mình cũng đã làm một số dự án với Nuxtjs của vuejs, đa số các dự án từng làm cần phải SEO nên việc sử dụng SSR (Server Side Rendering) cho web là thực sự cần thiết. Thời gian gần đây mình có tìm hiểu về Nextjs và thử sử dụng nó, thi thoảng thay đổi một chút =)). Ok, cả 2 framework đều sử dụng cơ chế SSR không vấn đề gì cả. Mình cũng đọc docs và bắt tay vào code bình thường nhưng ... 😄 😄 😄

Nếu bạn nào đã từng dùng Nuxtjs, VueApp, ReactApp hay Nextjs đều thấy rằng chúng đều là các app tách bạch hoàn toàn với Api, có router riêng. Bên Nuxtjs có hỗ trợ middleware thực hiện Authorization các router. Nuxtjs có hỗ trợ middleware cho chúng ta rồi. Bạn chỉ cần tạo file middleware:

export default function (context) {
  context.userAgent = process.server ? context.req.headers['user-agent'] : navigator.userAgent
}

Để sử dụng middleware cần khai báo trong page.vue là xong.

<template>
    //
</template>

export default {
  middleware: ['user-agent'],
}

Mới đầu mình nghĩ rằng Nuxtjs hỗ trợ viết dễ dàng như vậy thì Nextjs cũng thế, nhưng mình đã lầm =)), lục tìm, bới tung cả docs của Nextjs không thấy có =)). Chính vì thế bài viết này mình muốn giới thiệu cho các bạn một cách để viết middleware khi các bạn sử dụng Nextjs.

3. Giải quyết

Tư tưởng: Mình sẽ dùng context, React HOC và store redux để viết middleware cho các router page.

Cài đặt Nextjs:

npx create-next-app

Hoặc

npm install --save next react react-dom

Và thêm package.js như sau

{
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start"
  }
}

okie xong =)) Tiếp tục nào 😄

Tạo file middleware/auth.js Thực hiện tạo một HOC, dùng context để get auth store.

import React from 'react';
import { checkAuth } from '~/utils/auth';

export const WithAuthSync = (WrappedComponent, role) => class MiddlewareAuth extends React.Component {
    static async getInitialProps(ctx) {
        const { store } = ctx;
        if (!store.getState().get('auth').get('loaded')) {
            let unsubscribe;
            await new Promise((resolve) => {
                unsubscribe = store.subscribe(() => {
                    if (store.getState().get('auth').get('loaded')) {
                        resolve();
                    }
                });
            });
            unsubscribe();
        }

        const token = checkAuth(store.getState().get('auth'), role, ctx);
        const componentProps = WrappedComponent.getInitialProps
                && (await WrappedComponent.getInitialProps(ctx));

        return { ...componentProps, token };
    }

    render() {
        return (
            <WrappedComponent {...this.props} />
        );
    }
};

Tạo file utills/auth.js

import Router from 'next/router'

export const checkAuth = (auth, role, ctx) => {
  const user = auth.get('user')
  if (role === 'admin' && (user.size === 0 || !user.get('is_admin'))) {
    if (ctx.req) {
      ctx.res.writeHead(302, { Location: '/' })
      ctx.res.end()
    }
    if (!ctx.isServer) {
      Router.push('/')
    }
  }

  if (role === 'user' && user.size === 0) {
    if (ctx.req) {
      ctx.res.writeHead(302, { Location: '/' })
      ctx.res.end()
    }
    if (!ctx.isServer) {
      Router.push('/')
    }
  }
}

Tại đây sau khi đã được truyền auth store, role user, và context component, ta thực hiện check:

  • Server side: writeHead nếu user không có quyền truy cập
  • Client side: Sử dụng Router push.

Và sử dụng:

//admin page
import React from 'react';

import { WithAuthSync } from '~/middleware/auth';

const Dashboard = () => (
    <div>
        Dashboard
    </div>
);

export default WithAuthSync(Dashboard, 'admin');
// user page
import React from 'react';

import { WithAuthSync } from '~/middleware/auth';

const UserPage = () => (
    <div>
        Homepage
    </div>
);

export default WithAuthSync(UserPage, 'user');

Khá là đơn giản đúng không nào, giờ chúng ta cùng mở trang web lên và test nhé ❤️ ❤️ ❤️

Tạm kết

Chắc rằng sau bài viết các bạn đã có thể tạo middleware cho trang web sử dụng Next.js rồi đúng không nào. Hy vọng nhận được nhiều ý kiến đóng góp.


All Rights Reserved