📦 DEPRECATED 📦
This package has been deprecated and recommend to use "next-typed-routes" instead.
now-next-routes
🔜 Dynamic routes for Next.js on Now 🔙
// /routes.ts
export default {
"users/user": new Route<{ userId: number }>({ page: "/users/user", pattern: "/users/:userId" }),
"users/profile": new Route<{ userId: number }>({ page: "/users/profile", pattern: "/users/:userId/profile" }),
};
// /pages/users.tsx
import routes from "./routes.ts";
const userPageLinkProps = routes["users/user"].getLinkProps({ userId: 1 });
export default () => (
<Link {...userPageLinkProps}>
<a>Go to the first user page</a>
</Link>
);
// $ npx now-next-routes generate routes.ts
// then ... now.json is generated!!
//
// /now.json
{
...
"routes": [
{
"src": "^\\/users\\/([^\\/]+?)\\/?$",
"dest": "users/user?userId=$1"
},
{
"src": "^\\/users\\/([^\\/]+?)\\/profile\\/?$",
"dest": "users/profile?userId=$1"
}
]
}
Table of Contents
Features
FEATURES | WHAT YOU CAN DO |
---|---|
|
Don't need to manage routes property in now.json
|
|
Custom servers are not required |
|
All you need is write routes to one file |
|
You can get errors when missing required dynamic URL parameters |
Motivation
Next.js has file-system routing, but it does not provide dynamic routing. In the strict sense, it is possible to implement dynamic routing if you adopt custom servers such as Express.js. Famous libraries which provide dynamic routes such as next-routes and nextjs-dynamic-routes require to use custom servers too.
On the other hand, ZEIT that develops and manages Next.js provides FaaS called Now. I definitely think
it is the best way to hosting Next.js apps. Now adopts serverless architecture, so you can deploy Next.js app very easily, all
you need is to write now.json
with @now/next
.
ZEIT recommends to deploy one page on one lambda in order to get benefits of serverless
and @now/next
conforms it automatically. However you need use @now/node
or @now/node-server
if you adopt custom servers.
In other words, to use custom servers in Next.js on Now is bad.
The best way is to use no custom servers and set routes
property in now.json
to your application routes, but it has very low
maintainability because you need to write the same route settings twice and complex regular expressions.
now-next-routes was created in order to resolve these issues. now-next-routes provides a command to create routes
property in
now.json
from a specific route definitions and also provides functions to create links high type safety.
Quick Start
Requirements
- Node.js 8.0.0 or higher
- npm or Yarn
Installation
$ npm install --save-dev now-next-routes
If you use Yarn, use the following command.
$ yarn add --dev now-next-routes
Usage
Defines routes
// /routes.ts
import { Route } from "now-next-routes";
export default {
// Index
index: new Route({ page: "/index", pattern: "/" }),
// Users
"users/users": new Route({ page: "/users/users", pattern: "/users" }),
"users/user": new Route<{ userId: number }>({ page: "/users/user", pattern: "/users/:userId" }),
// Users - Comments
"comments/comment": new Route<{ userId: number; commentId: number }>({
page: "/comments/comment",
pattern: "/users/:userId/comments/:commentId",
}),
};
First, you need to define routes using now-next-routes. A point that must be keep in your mind is to export as default value
using export default
syntax.
Route
constructor
Arguments of now-next-routes exports Route
class which provides to create objects for Next.js and Now. A route should be an object which
has a page identifier as key and Route
object as value.
Currently, a constructor of Route
accepts an object which has two properties.
-
page: string
... A page of Next.js. This is a path of files in/pages
in general. -
pattern: string
... An actual URL pattern. Dynamic parameters should follow the format:parameterName
.
Gets more type safety
new Route<{ userId: number }>({ page: "/users/user", pattern: "/users/:userId" });
Also the constructor accepts one generic type. This type is to improve type safety when you create links with dynamic
parameters. If a route requires dynamic parameters and your project is written by TypeScript, I recommended to
specify.
The type should be an object literal type and properties should be matched dynamic parameters you write in pattern
.
Generates routes for Now
now-next-routes also provides a command to generate routes for Now.
$ npx now-next-routes generate routes.ts
generate
command generates routes
property of now.json
from a routes file you created like the following.
{
"routes": [
{
"src": "^\\/?$",
"dest": "index"
},
{
"src": "^\\/users\\/?$",
"dest": "users/users"
},
{
"src": "^\\/users\\/([^\\/]+?)\\/?$",
"dest": "users/user?userId=$1"
},
{
"src": "^\\/users\\/([^\\/]+?)\\/comments\\/([^\\/]+?)\\/?$",
"dest": "comments/comment?userId=$1&commentId=$2"
}
]
}
If now.json
exists, this command overwrite routes
property in it. Otherwise, this command creates now.json
file.
Creates links
import routes from "./routes.ts";
const userPageLinkProps = routes["users/user"].getLinkProps({ userId: 1 });
const Component = () => (
<Link {...userPageLinkProps}>
<a>Go to the first user page</a>
</Link>
);
An object of Route
class provides getLinkProps
function. This is to create props of built-in <Link>
component provided by
Next.js.
Specifically this function returns an object which has href
and as
properties. So you can also write like the following.
<Link href={userPageLinkProps.href} as={userPageLinkProps.as}>
Router.push()
Uses in Router.push()
function provided from next/router
makes you to be possible to move to some pages programmatically. This
function also accepts href
and as
values as arguments, so you can give parameters using getLinkProps()
.
Router.push(userPageLinkProps.href, userPageLinkProps.as);
Type safety
now-next-routes is designed by TypeScript. So you can create links type safety from routes you crated if you gives generic types when calling constructors.
const userPageLinkProps = routes["users/user"].getLinkProps({ userId: "1" });
^^^^^^
// Type 'string' is not assignable to type 'number'. ts(2322)
Recipes
Gets type safety in a page when handling queries
import { DynamicParameters } from "now-next-routes";
type Query = DynamicParameters<typeof routes["users/user"]>;
Component.getInitialProps = (ctx) => {
const query = ctx as Query;
const userId = query.user_id;
^^^^^^^
// Property 'user_id' does not exist on type 'Partial<{ userId: number; }>'.
// Did you mean 'userId'? ts(2551)
...
return { userId: query.userId };
};
DynamicParameters
type exported by now-next-routes accepts a type of Route
object. It returns a type of dynamic parameters.
It is helpful for handling queries type safety in getInitialProps
.
now.json
Preserves original By default, now-next-routes
overwrites or creates now.json
file. It is possible to output as other file when you specify
--input
or --output
option.
# Inputs from a template file and outputs as `now.json` .
$ npx now-next-routes generate --input now.sample.json routes.ts
# Inputs from `now.json` and outputs as a specific file name.
$ npx now-next-routes generate --output now-production.json routes.ts
If your now.json
already has custom routes
property, it is possible to merge generated routes to it and you should use
--merge
option.
API
CLI
$ npx now-next-routes --help
Usage: now-next-routes [options] [command]
Options:
-v, --version output the version number
-h, --help output usage information
Commands:
generate [options] <routes-file> generate routes for now.json
*
generate
$ npx now-next-routes generate --help (06/03 06:45:54)
Usage: generate [options] <routes-file>
generate routes for now.json
Options:
-i, --input <path> now.json file path
-o, --output <path> an output now.json file path
-m, --merge merge into existing "routes" property
-h, --help output usage information
Route
Class
constructor<Parameters>(settings: Settings): Route
new Route<{ userId: number }>({ page: "/users/user", pattern: "/users/:userId" });
Returns a new object of Route
class.
-
Parameters
... A generic type to specify types of dynamic parameters (for TypeScript). -
settings: Settings
-
page: string
... A page file path in/pages
directory of Next.js. -
pattern: string
... A mapping path pattern. The format:parameterName
will be mapped to dynamic parameters.
-
Route.prototype.getLinkProps(parameters: Parameters): LinkProps<Parameters>
route.getLinkProps({ userId: number });
Returns LinkProps<Parameters>
object.
-
Parameters
... Types of dynamic parameters you gave to constructor (for TypeScript). -
LinkProps<Parameters>
-
href: object
-
pathname: string
... A base path name. This is a page file path in/pages
in general. -
query: Partial<Parameters>
... Displayed URL string in web browsers.
-
-
as: string
... A mapped URL.
-
Route.prototype.getPaths(parameters: Parameters): Paths
route.getPaths({ userId: number });
Returns Paths
object.
-
Parameters
... Types of dynamic parameters you gave to constructor (for TypeScript). -
Paths
-
href: string
... A full path which contains a page file path in/pages
and query string. -
as: string
... A mapped URL.
-
Route.prototype.createRouteForNow(): object
route.createRouteForNow();
Creates an object for routes
property in now.json
for ZEIT Now. This method is used in CLI, so you don't need to use this
method in general.
This method will be changed without any notices.
DynamicParameters<T extends Route>
type Parameters = { userId: number };
const route = new Route<Parameters>({ page: "users/user", pattern: "/users/:userId" })
type Query = DynamicParameters<typeof route>; // Partial<{ userId: number }>
Returns a type you gave to a constructor of Route
. A returned type is weak type.
Contributing to now-next-routes
Bug reports and pull requests are welcome on GitHub at https://github.com/jagaapple/now-next-routes. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
Please read Contributing Guidelines before development and contributing.
License
The library is available as open source under the terms of the MIT License.
Copyright 2019 Jaga Apple. All rights reserved.