-
Notifications
You must be signed in to change notification settings - Fork 0
API Calls
In Next.js
pages and components, a lot of the code may run either on the client or the server.
This is an major advantage for Next.js
to reduce duplicated code, but it can also make it
slightly harder when a different behavior on the server/client is desired.
In some cases it is possible to tell where the code is running on,
e.g. the UI event handler will only run on the client and some functions like getStaticProps
will only run on the server. But in general, doing different things on the server/client
requires explicitly detecting the environment, e.g. with process.browser
.
A common and generalizable case is when calling the a server-side function when creating the UI. In this case, the input/output from the caller as well as the code that actually needs to run are exactly the same. The only difference is how the call can be routed (i.e. direct calling vs sending a POST request) so we should be able to abstract this out without having the caller doing anything special. (It may not be as simple as calling a normal function but the interface will be the same no matter where the code runs.)
The main challenge for this is that since webpack automatically loads/translates/compiles
all code used by each page, we have to prevent it from grabbing server-only code
that is impossible to run on the client side (e.g. it's accessing local file/database
or calling a C library). Webpack provides an escape hatch for this
which is to hide the require
call behind a process.browser
branch,
i.e. we can create a client and server version of the module and
let the user go through a wrapper module defined as,
module.exports = (process.browser ? require('client_module') : require('server_module'));
This makes sure the client and the server module are only ever being loaded
on the client or server side respectively.
(Note that we use this trick not only for the API calls but also for other functions
that needs a different version on the client vs server,
e.g. crypto/hash functions).
We additionally configure webpack to not look into the server module
by putting them in server/
and treating everything there as external modules
that are not to be translated. We also force the module to be loaded as null
on the client side to detect any possible errors.
The actual server API call interface is implemented
by passing the arguments and results as objects.
When calling from the server, the arguments and results
are passed between the caller and the real API implementation directly
whereas a caller from the client will pass the objects between the server and client
via JSON string using a POST request on the /api
path.
Each API entry points has a name which is used as the key
in the object for the argument and the result,
i.e. to call the API api_name_1
one would do api({api_name_1: params_for_api})
and the returned value would have the form {api_name_1: result_for_api}
.
The caller can pass in an object with more than one fields
to potentially call more than one functions with a single request to the server.
Each API corresponds to a module under apis/
which is automatically loaded by the server
so that we don't need to manually construct a big lookup table
to find the correct function by name.
In order to support sessions, the API implementations can access
the request and the reply objects in order to read and modify cookies.
For a direct server side call, as long as the call is always made as a response
to a client request, these objects should be accessible from the Next.js
API.
For a client side request, these are directly filled in by the server code that handles
the /api
request.