Skip to content

unfoldadmin/turbo

Repository files navigation

Turbo - Django & Next.js boilerplate

Turbo is a simple bootstrap template for Django and Next.js, combining both frameworks under one monorepository, including best practices.

Features

  • Microsites: supports several front ends connected to API backend
  • API typesafety: exported types from backend stored in shared front end package
  • Server actions: handling form submissions in server part of Next project
  • Tailwind CSS: built-in support for all front end packages and sites
  • Docker Compose: start both front end and backend by running docker compose up
  • Auth system: incorporated user authentication based on JWT tokens
  • Profile management: update profile information from the front end
  • Registrations: creation of new user accounts (activation not included)
  • Admin theme: Unfold admin theme with user & group management
  • Custom user model: extended default Django user model
  • Visual Studio Code: project already constains VS Code containers and tasks

Table of contents

Quickstart

To start using Turbo, it is needed to clone the repository to your local machine and then run docker compose, which will take care about the installation process. The only prerequisite for starting Turbo template is to have docker compose installed and preconfiguring files with environment variables.

git clone https://github.com/unfoldadmin/turbo.git
cd turbo

Environment files configuration

Before you can run docker compose up, you have to set up two files with environment variables. Both files are loaded via docker compose and variables are available within docker containers.

cp .env.backend.template .env.backend # set SECRET_KEY and DEBUG=1 for debug mode on
cp .env.frontend.template .env.frontend # set NEXTAUTH_SECRET to a value "openssl rand -base64 32"

For more advanced environment variables configuration for the front end, it is recommended to read official Next.js documentation about environment variables where it is possible to configure specific variables for each microsite.

On the backend it is possible to use third party libraries for loading environment variables. In case that loading variables through os.environ is not fulfilling the requriements, we recommend using django-environ application.

Running docker compose

docker compose up

After successful installation, it will be possible to access both front end (http://localhost:3000) and backend (http://localhost:8000) part of the system from the browsers.

NOTE: Don't forget to change database credentials in docker-compose.yaml and in .env.backend by configuring DATABASE_PASSWORD.

Included dependencies

The general rule when it comes to dependencies is to have minimum of third party applications or plugins to avoid future problems updating the project and keep the maintenance of applications is minimal.

Backend dependencies

For dependency management in Django application we are using Poetry. When starting the project through the docker compose command, it is checked for new dependencies as well. In the case they are not installed, docker will install them before running development server.

Below, you can find a command to install new dependency into backend project.

docker compose exec api poetry add djangorestframework

Front end dependencies

For the frontend project, it is bit more complicated to maintain fron end dependencies than in backend part. Dependencies, can be split into two parts. First part are general dependencies available for all projects under packages and apps folders. The second part are dependencies, which are project specific.

To install a global dependency for all packages and apps, use -w parameter. In case of development package, add -D argument to install it into development dependencies.

docker compose exec web pnpm add react-hook-form -w

To install a dependency for specific app or package, use --filter to specify particular package.

docker compose exec web pnpm --filter web add react-hook-form

Front end project structure

Project structure on the front end, it is quite different from the directory hierarchy in the backend. Turbo counts with an option that front end have multiple front ends available on various domains or ports.

frontend
| - apps       // available sites
|   - web      // available next.js project
| - packages   // shared packages between sites
|   - types    // exported types from backend - api
|   - ui       // general ui components

The general rule here is, if you want to have some shared code, create new package under packages/ folder. After adding new package and making it available for your website, it is needed to install the new package into website project by running a command below.

docker compose exec web pnpm --filter web add @frontend/ui

Adding microsite to docker-compose.yaml

If you want to have new website facing customers, create new project under apps/ directory. Keep in mind that docker-compose.yaml file must be adjusted to start a new project with appropriate new port.

new_microsite:
  command: bash -c "pnpm install -r && pnpm --filter new_microsite dev"
  build:
    context: frontend # Dockerfile can be same
  volumes:
    - ./frontend:/app
  expose:
    - "3001" # different port
  ports:
    - "3001:3001" # different port
  env_file:
    - .env.frontend
  depends_on:
    - api

Authentication

For the authentication, Turbo uses django-simplejwt and next-auth package to provide simple REST based JWT authentication. On the backend, there is no configuraton related to django-simplejwt so everything is set to default values.

On the front end, next-auth is used to provide credentials authentication. The most important file on the front end related to authentication is frontend/web/src/lib/auth.ts which is containing whole business logic behind authentication.

Configuring env variables

Before starting using authentication, it is crucial to configure environment variable NEXTAUTH_SECRET in .env.frontend file. You can set the value to the output of the command below.

openssl rand -base64 32

User accounts on the backend

There are two ways how to create new user account in the backend. First option is to run managed command responsible for creating superuser. It is more or less required, if you want to have an access to the Django admin. After running the command below, it will be possible to log in on the front end part of the application.

docker compose exec api poetry run python src/manage.py createsuperuser

The second option how to create new user account is to register it on the front end. Turbo provides simple registration form. After account registration, it will be not possible to log in because account is inactive. Superuser needs to access Django admin and activate an account. This is a default behavior provided by Turbo, implementation of special way of account activation is currently out the scope of the project.

Authenticated paths on frontend

To ensure path is only for authenticated users, it is possible to use getServerSession to check the status of user.

This function accepts an argument with authentication options, which can be imported from @/lib/auth and contains credentials authentication business logic.

import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";
import { authOptions } from "@/lib/auth";

const SomePageForAuthenticatedUsers = async () => {
  const session = await getServerSession(authOptions);

  if (session === null) {
    return redirect("/");
  }

  return <>content</>;
};

To require authenticated user account on multiple pages, similar business logic can be applied in layouts.tsx.

import { redirect } from "next/navigation";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";

const AuthenticatedLayout = async ({
  children,
}: {
  children: React.ReactNode;
}) => {
  const session = await getServerSession(authOptions);

  if (session === null) {
    return redirect("/");
  }

  return <>{children}</>;
};

export default AuthenticatedLayout;

API calls to backend

Currently Turbo implements Next.js server actions in folder frontend/apps/web/src/actions/ responsible for communication with the backend. When the server action is hit from the client, it fetches required data from Django API backend.

API Client

The query between server action and Django backend is handled by using an API client generated by openapi-typescript-codegen package. In Turbo, there is a function getApiClient available in frontend/apps/web/src/lib/api.ts which already implements default options and authentication tokens.

Updating OpenAPI schema

After changes on the backend, for example adding new fields into serializers, it is required to update typescript schema on the frontend. The schema can be updated by running command below. In VS Code, there is prepared task which will update definition.

docker compose exec web pnpm openapi:generate

Swagger

By default, Turbo includes Swagger for API schema which is available here http://localhost:8000/api/schema/swagger-ui/. Swagger can be disabled by editing urls.py and removing SpectacularSwaggerView.

Client side requests

At the moment, Turbo does not contain any examples of client side requests towards the backend. All the requests are handled by server actions. For client side requests, it is recommended to use react-query.

Developing in VS Code

The project contains configuration files for devcontainers so it is possible to directly work inside the container within VS Code. When the project opens in the VS Code the popup will appear to reopen the project in container. An action Dev Containers: Reopen in Container is available as well. Click on the reopen button and select the container which you want to work on. When you want to switch from the frontend to the backend project run Dev Containers: Switch container action. In case you are done and you want to work in the parent folder run Dev Containers: Reopen Folder Locally action