Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      UX Job Interview Helpers

      August 5, 2025

      .NET Aspire’s CLI reaches general availability in 9.4 release

      August 5, 2025

      15 Essential Skills to Look for When Hiring Node.js Developers for Enterprise Projects (2025-2026)

      August 4, 2025

      African training program creates developers with cloud-native skills

      August 4, 2025

      Why I’ll keep the Samsung Z Fold 7 over the Pixel 10 Pro Fold – especially if these rumors are true

      August 5, 2025

      You may soon get Starlink internet for a much lower ‘Community’ price – here’s how

      August 5, 2025

      uBlock Origin Lite has finally arrived for Safari – with one important caveat

      August 5, 2025

      Perplexity says Cloudflare’s accusations of ‘stealth’ AI scraping are based on embarrassing errors

      August 5, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Send Notifications in Laravel with Firebase Cloud Messaging and Notifire

      August 5, 2025
      Recent

      Send Notifications in Laravel with Firebase Cloud Messaging and Notifire

      August 5, 2025

      Simplified Batch Job Creation with Laravel’s Enhanced Artisan Command

      August 5, 2025

      Send Notifications in Laravel with Firebase Cloud Messaging and Notifire

      August 5, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      This comfy mesh office chair I’ve been testing costs less than $400 — but there’s a worthy alternative that’s far more affordable

      August 5, 2025
      Recent

      This comfy mesh office chair I’ve been testing costs less than $400 — but there’s a worthy alternative that’s far more affordable

      August 5, 2025

      How to get started with Markdown in the Notepad app for Windows 11

      August 5, 2025

      Microsoft Account Lockout: LibreOffice Developer’s Week-Long Nightmare Raises Concerns

      August 5, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to Deploy a Next.js API to Production using Sevalla

    How to Deploy a Next.js API to Production using Sevalla

    August 2, 2025

    When people hear about Next.js, they often think of server-side rendering, React-powered frontends, or SEO-optimised static websites. But there’s more to this powerful framework than just front-end development.

    Next.js also allows developers to build robust, scalable backend APIs directly inside the same codebase. This is especially valuable for small to mid-sized applications where having a tightly coupled frontend and backend speeds up development and deployment.

    In this article, you’ll learn how to build an API using Next.js and deploy it to production using Sevalla. It’s relatively easy to learn how to build something using a tutorial – but the real challenge is to get it into the hands of users. Doing so transforms your project from a local prototype into something real and usable.

    Table of Contents

    • What is Next.js?

    • Installation & Setup

    • How to Build a REST API

    • How to Test the API

    • How to Deploy to Sevalla

    • Conclusion

    What is Next.js?

    Next.js is an open-source React framework built by Vercel. It enables developers to build server-rendered and statically generated web applications.

    It essentially abstracts the configuration and boilerplate needed to run a full-stack React application, making it easier for developers to focus on building features rather than setting up infrastructure.

    While it started as a solution for frontend challenges in React, it has evolved into a full-stack framework that lets you handle backend logic, interact with databases, and build APIs. This unified codebase is what makes Next.js particularly compelling for modern web development.

    Installation & Setup

    Let’s install Next.js. Make sure you have Node.js and NPM installed on your system, and that they’re the latest version.

    $ node --version
    v22.16.0
    
    $ npm --version
    10.9.2
    

    Now let’s create a Next.js project. The command to do so is:

    $ npx create-next-app@latest
    

    The result of the above command will ask you a series of questions to setup your app:

    What is your project named? my-app
    Would you like to use TypeScript? No / Yes
    Would you like to use ESLint? No / Yes
    Would you like to use Tailwind CSS? No / Yes
    Would you like your code inside a `src/` directory? No / Yes
    Would you like to use App Router? (recommended) No / Yes
    Would you like to use Turbopack for `next dev`?  No / Yes
    Would you like to customize the import alias (`@/*` by default)? No / Yes
    What import alias would you like configured? @/*
    

    But for this tutorial, we aren’t interested in a full stack app – just an API. So let’s re-create the app using the — - api flag.

    $ npx create-next-app@latest --api
    

    It will still ask you a few questions. Use the default settings and finish creating the app.

    Next.js API setup

    Once the setup is done, you can see the folder with your app name. Let’s go into the folder and run the app.

    $ npm run dev
    

    Your API template should be running at port 3000. Go to http://localhost:3000 and you should see the following message:

    {
    "message": "Hello world!"
    }
    

    How to Build a REST API

    Now that we’ve set up our API template, let’s write a basic REST API. A basic REST API is simply four endpoints: Create, Read, Update, Delete (also called as CRUD).

    Usually, we’d use a database, but for simplicity’s sake, we’ll use a JSON file in our API. Our goal is to build a REST API that can read and write to this JSON file.

    The API code will reside under /app within the project directory. Next.js uses file-based routing for building URL paths.

    For example, if you want a URL path /users, you should have a directory called “users” with a route.ts file to handle all the CRUD operations for /users. For /users/:id, you should have a directory called [id] under “users” directory with a route.ts file. The square brackets are to tell Next.js that you expect dynamic values for the /users/:id route.

    You should also have the users.json inside the /app/users directory for your routes to read and write data.

    Here is a screenshot of the setup. Delete the [slug] directory that comes with the project since it won’t be relevant for us:

    Route Structure

    • The route.ts file at the bottom handles CRUD operations for /

    • The route.ts file under/users handles CRUD operations for /users

    • The route.ts file under /users/[id]/ handles CRUD operations under /users/:id where the ‘id’ will be a dynamic value.

    • The users.json under /users will be our data store.

    While this setup can seem complicated for a simple project, it provides a clear structure for large-scale web applications. If you want to go deeper into building complex APIs with Next.js, here is a tutorial you can follow.

    The code under /app/route.ts is the default file for our API. You can see it serving the GET request and responding with “Hello World!”:

    import { NextResponse } from "next/server";
    
    export async function GET() {
      return NextResponse.json({ message: "Hello world!" });
    }
    

    Now we need five routes:

    • GET /users → List all users

    • GET /users/:id → List a single user

    • POST /users → Create a new user

    • PUT /users/:id → Update an existing user

    • DELETE /users/:id → Delete an existing user

    Here is the code for the route.ts file under /app/users:

    import { NextResponse } from "next/server";
    import type { NextRequest } from "next/server";
    import { promises as fs } from "fs"; // Importing promise-based filesystem methods
    import path from "path"; // For handling file paths
    
    // Define the structure of a User object
    interface User {
      id: string;
      name: string;
      email: string;
      age: number;
    }
    
    // Define the path to the users.json file
    const usersFile = path.join(process.cwd(), "app/users/users.json");
    
    // Read users from the JSON file and return them as an array
    async function readUsers(): Promise<User[]> {
      try {
        const data = await fs.readFile(usersFile, "utf-8");
        return JSON.parse(data) as User[];
      } catch {
        // If file doesn't exist or fails to read, return empty array
        return [];
      }
    }
    
    // Write updated users array to the JSON file
    async function writeUsers(users: User[]) {
      await fs.writeFile(usersFile, JSON.stringify(users, null, 2), "utf-8");
    }
    
    // Handle GET request: return list of users
    export async function GET() {
      const users = await readUsers();
      return NextResponse.json(users);
    }
    
    // Handle POST request: add a new user
    export async function POST(request: NextRequest) {
      const body = await request.json();
    
      // Destructure and validate input fields
      const { name, email, age } = body as {
        name?: string;
        email?: string;
        age?: number;
      };
    
      // Return 400 if any required field is missing
      if (!name || !email || age === undefined) {
        return NextResponse.json(
          { error: "Missing name, email, or age" },
          { status: 400 }
        );
      }
    
      // Read existing users
      const users = await readUsers();
    
      // Create new user object with unique ID based on timestamp
      const newUser: User = {
        id: Date.now().toString(),
        name,
        email,
        age,
      };
    
      // Add new user to the list and save to file
      users.push(newUser);
      await writeUsers(users);
    
      // Return the newly created user with 201 Created status
      return NextResponse.json(newUser, { status: 201 });
    }
    

    Now the code for the /app/users/[id]/route.ts file:

    import { NextResponse } from "next/server";
    import type { NextRequest } from "next/server";
    import { promises as fs } from "fs";
    import path from "path";
    
    // Define the User interface
    interface User {
      id: string;
      name: string;
      email: string;
      age: number;
    }
    
    // Path to the users.json file
    const usersFile = path.join(process.cwd(), "app/users/users.json");
    
    // Function to read users from the JSON file
    async function readUsers(): Promise<User[]> {
      try {
        const data = await fs.readFile(usersFile, "utf-8");
        return JSON.parse(data) as User[];
      } catch {
        // If file doesn't exist or is unreadable, return an empty array
        return [];
      }
    }
    
    // Function to write updated users to the JSON file
    async function writeUsers(users: User[]) {
      await fs.writeFile(usersFile, JSON.stringify(users, null, 2), "utf-8");
    }
    
    // GET /users/:id - Fetch a user by ID
    export async function GET(
      request: NextRequest,
      { params }: { params: Promise<{ id: string }> },
    ) {
      const id = (await params).id;
      const users = await readUsers();
    
      // Find the user by ID
      const user = users.find((u) => u.id === id);
    
      // Return 404 if user is not found
      if (!user) {
        return NextResponse.json({ error: "User not found" }, { status: 404 });
      }
    
      // Return the found user
      return NextResponse.json(user);
    }
    
    // PUT /users/:id - Update a user by ID
    export async function PUT(
      request: NextRequest,
      { params }: { params: Promise<{ id: string }> },
    ) {
      const id = (await params).id;
      const body = await request.json();
    
      // Extract optional fields from request body
      const { name, email, age } = body as {
        name?: string;
        email?: string;
        age?: number;
      };
    
      const users = await readUsers();
    
      // Find the index of the user to update
      const index = users.findIndex((u) => u.id === id);
    
      // Return 404 if user not found
      if (index === -1) {
        return NextResponse.json({ error: "User not found" }, { status: 404 });
      }
    
      // Update the user only with provided fields
      users[index] = {
        ...users[index],
        ...(name !== undefined ? { name } : {}),
        ...(email !== undefined ? { email } : {}),
        ...(age !== undefined ? { age } : {}),
      };
    
      await writeUsers(users);
    
      // Return the updated user
      return NextResponse.json(users[index]);
    }
    
    // DELETE /users/:id - Delete a user by ID
    export async function DELETE(
      request: NextRequest,
      { params }: { params: Promise<{ id: string }> },
    ) {
      const id = (await params).id;
      const users = await readUsers();
    
      // Find the index of the user to delete
      const index = users.findIndex((u) => u.id === id);
    
      // Return 404 if user not found
      if (index === -1) {
        return NextResponse.json({ error: "User not found" }, { status: 404 });
      }
    
      // Remove user from the array and save updated list
      const [deleted] = users.splice(index, 1);
      await writeUsers(users);
    
      // Return the deleted user
      return NextResponse.json(deleted);
    }
    

    We will have an empty array inside the /app/users.json. You can find all the code here in this repository.

    How to Test the API

    Now let’s test the API endpoints.

    First, lets run the API:

    $ npm run dev
    

    You can go to http://localhost:3000/users and can see an empty array since we have not pushed any user information.

    From the code, we can see that a user object needs name, email, and age since the id is automatically generated in the POST endpoint.

    New user creation

    We will use Postman to simulate requests to the API and ensure that the API behaves as expected.

    1. GET /users: it will be empty on our first try since we haven’t pushed any data yet.

    GET /users

    1. POST /users: create a new user. Under “body”, choose “raw” and select “JSON”. This is the data we will be sending the api. The JSON body would be
    {"name":"Manish","age":30, "email":"manish@example.com"}
    

    POST /users

    I’ll create one more record named “Larry”. Here is the JSON:

    {"name":"Larry","age":25, "email":"larrry@example.com"}
    

    Now let’s look at the users. You should see two entries for our GET request to /users:

    GET /users

    Now let’s look at a single user using /users/:id.

    GET /users/:id

    Now let’s update Larry’s age to 35. We’ll pass just the age in request body using the PUT request to /users/:id.

    PUT /users/:id

    Now let’s delete Larry’s record.

    DELETE /users/:id

    If you check /users, you should see only one record:

    GET /users

    So we have built and tested our api. Now let’s get this live.

    How to Deploy to Sevalla

    Sevalla is a modern, usage-based Platform-as-a-service provider and an alternative to sites like Heroku or to your self-managed setup on AWS. It combines powerful features with a smooth developer experience.

    Sevalls offers application hosting, database, object storage, and static site hosting for your projects. It comes with a generous free tier, so let’s see how to deploy our API to the cloud using Sevalla.

    Make sure you have the code committed to GitHub or fork my repository for this project. If you are new to Sevalla, you can sign up using your GitHub account to enable direct deploys from your GitHub account. Every time you push code to your project, Sevalla will auto-pull and deploy your app to the cloud.

    Once you login to Sevalla, click on “Applications”. Now let’s create an app.

    Sevalla App interface

    If you have authenticated with GitHub, the application creation interface will display a list of repositories. Choose the one you pushed your code into or the nextjs-api project if you forked it from my repository.

    Connect Repository

    Check the box “auto deploy on commit”. This will ensure your latest code is auto-deployed to Sevalla. Now, let’s choose the instance to which we can deploy the application. Each one comes with its own pricing, based on the server’s capacity.

    Let’s choose the hobby server that costs $5/mo. Sevalla gives us a $50 free tier, so we don’t have to pay for anything unless we exceed this usage tier.

    Sevalla usage tier

    Now, click “Create and Deploy”. This should pull our code from our repository, run the build process, setup a Docker container and then deploy the app. Usually the work of a sysadmin, fully automated by Sevalla.

    Wait for a few minutes for all the above to happen. You can watch the logs in the “Deployments” interface.

    Application deployment

    Now, click on “Visit App” and you will get the live URL (ending with sevalla.app) of your API. You can replace “http://localhost:3000” with the new URL and run the same tests using Postman.

    Congratulations – your app is now live. You can do more with your app using the admin interface, like:

    • Monitor the performance of your app

    • Watch real-time logs

    • Add custom domains

    • Update network settings (open/close ports for security, and so on)

    • Add more storage

    Sevalla also provides resources like Object storage, database, cache, and so on, which are out of scope for this tutorial. But it lets you monitor, manage, and scale your application without the need for a system administrator. That’s the beauty of PaaS systems. Here is a detailed comparison of VPS vs PaaS systems for application hosting.

    Conclusion

    In this article, we went beyond the typical frontend use case of Next.js and explored its capabilities as a full-stack framework. We built a complete REST API using the App Router and file-based routing, with data stored in a JSON file. Then, we took it a step further and deployed the API to production using Sevalla, a modern PaaS that automates deployment, scaling, and monitoring.

    This setup demonstrates how developers can build and ship full-stack applications like frontend, backend, and deployment, all within a single Next.js project. Whether you’re prototyping or building for scale, this workflow sets you up with everything you need to get your apps into users’ hands quickly and efficiently.

    Hope you enjoyed this article. I ll see you soon with another one. Connect with me on LinkedIn or visit my website.

    Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleEmbedded Swift: A Modern Approach to Low-Level Programming
    Next Article How Infinite Loops Work in C++

    Related Posts

    Development

    Send Notifications in Laravel with Firebase Cloud Messaging and Notifire

    August 5, 2025
    Development

    Simplified Batch Job Creation with Laravel’s Enhanced Artisan Command

    August 5, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    Windows 11 24H2 emergency update fixes Easy Anti-Cheat BSOD issue

    Security

    CVE-2025-6989 – Kallyas WordPress Arbitrary Folder Deletion

    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-46532 – Haris Zulfiqar Tooltip Cross-site Scripting (XSS)

    Common Vulnerabilities and Exposures (CVEs)

    Understanding Mobile App Brand Awareness: Metrics and Strategies

    Web Development

    Highlights

    CVE-2025-48060 – jq Heap Buffer Overflow Vulnerability

    May 21, 2025

    CVE ID : CVE-2025-48060

    Published : May 21, 2025, 6:15 p.m. | 2 hours, 26 minutes ago

    Description : jq is a command-line JSON processor. In versions up to and including 1.7.1, a heap-buffer-overflow is present in function `jv_string_vfmt` in the jq_fuzz_execute harness from oss-fuzz. This crash happens on file jv.c, line 1456 `void* p = malloc(sz);`. As of time of publication, no patched versions are available.

    Severity: 0.0 | NA

    Visit the link for more details, such as CVSS details, affected products, timeline, and more…

    CVE-2025-6336 – TOTOLINK EX1200T HTTP POST Request Handler Buffer Overflow

    June 20, 2025

    CVE-2025-5630 – D-Link DIR-816 Remote Stack-Based Buffer Overflow Vulnerability

    June 4, 2025

    CVE-2025-2718 – CVE-2018-3627: Adobe Flash Player Cross-Site Scripting

    July 5, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.