Support for creating RESTful APIs with Netlify Functions

Hey there! Love Netlify. Great service. There is nothing better than an opinionated tool with sensible defaults that removes the need for cumbersome configs.

However! There is one sensible default that consistently causes me trouble: the way Netlify sets up API endpoints based on file names. It’s fine for a quick prototype, but even with prototypes I prefer to follow RESTful conventions for my APIs.

So let’s say I have a simple CRUD app with some users, I want endpoints to create, fetch one, fetch all, and update. Ideally, I would build the API to look like this:

POST /users       -> Create a user
GET  /users       -> Fetch all users
GET  /users/{id}  -> Fetch one user
PUT  /users/{id}  -> Update a user

As near as I can tell, it is impossible to create an API like this while also having each endpoint point to a different Netlify Function. I could create a single users.js lambda and put a router in it, but that would seem to defeat the purpose of serverless lambdas.

So while I love the zero-config convenience of naming routes after file names by default, I would love it if there were some way to support common RESTful conventions, even if it means some extra configuration.

I have three ideas about how this could be accomplished. Implementing one or all might be worthwhile.

Support Redirects Based On HTTP Method

I can already get halfway there with the current redirect support. For example, I could create a netlify.toml like this:

[[redirects]]
from = "/.netlify/functions/users/:id/"
to = "/.netlify/functions/fetch-one-user/:id/"

[[redirects]]
from = "/.netlify/functions/users"
to = "/.netlify/functions/fetch-all-users"

This works if all I am doing is fetching, or if every endpoint happens to have a unique URI. However, without some support for redirecting on HTTP methods, I am stuck adding some sort of routing functionality to my lambdas. Adding a method key to redirects would be very useful for this case, and probably for other use cases as well.

Example usage of the proposed method property:

[[redirects]]
from = "/.netlify/functions/users/:id/"
to = "/.netlify/functions/update-user/:id/"
method = "PUT"

[[redirects]]
from = "/.netlify/functions/users/:id/"
to = "/.netlify/functions/fetch-one-user/:id/"
method = "GET"

[[redirects]]
from = "/.netlify/functions/users"
to = "/.netlify/functions/fetch-all-users"
method = "GET"

[[redirects]]
from = "/.netlify/functions/users"
to = "/.netlify/functions/create-user"
method = "POST"

If you do not specify a method, then the redirect would apply to all methods as it does now. I am not sure how difficult this is to implement, but from an API perspective, it seems like a straightforward non-breaking change.

Introduce file name conventions for methods and path variables

This one might need a little bit more design, since it is a potentially breaking change. However, you could expand the rules you have around filenames to include HTTP methods and nested routes representing path variables.

For the example above, you might create a directory structure like this:

functions/
└─┬ users/
  ├── get.js
  └── post.js

That’s straightforward enough. But inevitably, it does get a bit weird once you introduce path variables. You might use some convention like underscores or curly braces to designate path variables.

functions/
└─┬ users/
  ├── get.js
  ├── post.js
  └─┬ __id__/
    ├── get.js
    └── put.js

Kind of ugly. But potentially useful. You might also use underscores or other special characters for the HTTP method based filenames as well to prevent breaking anyone’s deploys who happen to be using those names already.

Add new config options for manually specifying endpoints

This is kind of an anti-Netlify approach, but this would be an entirely optional addition, and it is not like Netlify is truly zero-config anyway. I don’t have any strong opinions on the exact syntax, but I imagine it would go in netlify.toml and might take queues from something like the Serverless framework’s serverless.yaml file:

[[functions]]
handler = "functions/update-user"
method = "PUT"
path = "/users/:id"

[[functions]]
handler = "functions/fetch-one-user"
method = "GET"
path = "/users/:id"

[[functions]]
handler = "functions/fetch-all-users"
method = "GET"
path = "/users"

[[functions]]
handler = "functions/create-user"
method = "POST"
path = "/users"

This is probably more work to implement, but since it is optional, it would not break anything.

Anyway! Hope that’s helpful. As I said, overall I love Netlify, and recommend it highly to whoever will listen. A little more robust support for naming backend endpoints would really be the icing on the cake.

4 Likes

Hello @delventhalz, welcome to the community!

Thanks for the very details post. But I think perhaps this blog post may help you get most of the way to where you want: How to run Express.js apps with Netlify Functions. That article describes using express within a lambda function. It’s a bit old (2018) but should still serve a good place to start.

Let me know if that helps.

Thanks for the response @Dennis! This isn’t exactly what I’m looking for. My hope is to use Netlify to take a lambda-based approach to API development. I can definitely load up a single lambda with a router (express.js or otherwise) and run all traffic through that one function. Ideally though, I would like to pair each RESTful method/path combo with its own purpose-built lambda.

That is not currently possible with Netlify, though it is possible on AWS with something like the Serverless framework.

I’m looking to achieve something similar, it’s not impossible. My approach thus far has been to structure the src directory files similar to your example.

I wrote a little Rollup plugin which actually reads the file names in the src and writes them to the accepted single level deep structure that Netlify requires. It dies this by prepending the folder name the function lives within, eg:

api/name/get.js
api/name/all.js
api/name/edit.js

Outputs

public/name-get.js
public/name-all.js
public/name-edit.js

Then I apply that redirect, to a _redirect file eg:

/name/get /.netlify/functions/… 200
etc
etc

(Sorry, I’m typing on phone)

if I get time I’ll OS the plugin I hacked up for this. It actually consumes an object export where I define the function and redirect options so I can extend upon some redirects, apply params etc etc.

Hope this helps.

2 Likes

thank you for sharing this!

Thanks @sissel! Yeah, this sounds similar to the workaround I am using right now with redirects. Sounds like the plumbing you have hooked up is handy though. Definitely let me know if you OS it.