Providing next event as API with Eleventy’s Global Data Files and Netlify Functions
Code in this blog post was written with versions: eleventy: 3.0.0, netlify-cli: 18.0.1

I have previously written about how I use Eleventy’s global data files to build community websites. For a while now, I’ve wanted to provide a programmatic way to read the next event information to use in other websites and this week I finally undertook the project. What started as a project to combine Eleventy’s global data files with Netlify’s serverless Functions turned into an interesting exploration about API design. I’ll write about both of those in this post.
Two key design motivators were:
- I wanted it to be automated without the webmaster having to remember to change yet another file.
- I wanted it to be easy for the consumer of the API to get this individual event date without having to do any logic in their code.
Part 1: Let’s build it
1 Prerequisites
To run the code in this project, you need to have
npm and
Netlify CLI
installed. Once you have npm, you can install Netlify CLI with
npm install -g netlify-cli
. This will
install it as a global CLI tool that you can use across your projects.
2 Project set up
I’ve built
a demo repository for this project
so you can see the entire code. You can clone that project or follow up by
building from scratch. If you clone the project, you need to run
npm install
in the repository before
continuing and skip the rest of this part 2.
First, we’ll need to create an npm project and install Eleventy by running these two commands in the command line:
npm init -y
npm install @11ty/eleventy
Next, we need to do two changes to our
package.json
file:
-
Change
type
field to"module"
to enable ESM style Javascript -
Add a
build
field toscripts
and set it to"eleventy"
To set up global data files in Eleventy, create a directory
_data/
into the project root and add a
file events.json
inside it:
[
{
"name": "April",
"date": "2025-04-20"
},
{
"name": "May",
"date": "2025-05-20"
},
{
"name": "June",
"date": "2025-06-20"
}
]
This will allow us to access the data from anywhere in our Eleventy project.
If you’re running these examples later than June 20th, 2025, you need to manually adjust the dates to add at least one event in a future date.
4 Create index file
To be able to access the global data files in the configuration file (that we’ll do in a bit), we need to use it somewhere in the templates. I’m not quite sure yet why that happens but it will prevent us from getting errors later on.
Create index.njk
in the project root and
add the following to it:
<ul>
{% for event in events %}
<li>{{ event.date }}: {{event.name}}</li>
{% endfor %}
</ul>
This will display the dates and events in a list on the front page of the
project. If you’re adding this functionality to an existing project, just make
sure events
is being accessed somewhere.
5 Set up Netlify Functions
Netlify Functions
will by default look for a folder
netlify/functions
. There you can store
multiple different functions in separate folders. Let’s create our first
Function by creating the necessary folders:
mkdir -p netlify/functions/api
-p
flag tells
mkdir
to create any necessary missing
folders along the path.
We’ll leave the folder empty for now as we’ll be dynamically creating the Javascript file itself as part of our build process.
We should then add this folder into
.eleventyignore
to avoid dev server
running around in an endless loop every time we change something (as a change
will trigger a build which will change api.js which will trigger a build and
so on and so on):
# Ignore here to avoid triggering a rebuild loop.
netlify
Netlify Functions folder will be configured in Netlify’s end when creating a
site so it doesn’t get built in into the
_site/
folder.
6 Create Eleventy config
Eleventy’s config lives in
eleventy.config.js
and the function
exported from that file will be run on each build.
// Import Node's file system library
import fs from 'fs'
export default function (eleventyConfig) {
// We create a new collection to gain access to collectionApi
eleventyConfig.addCollection('next_event', collectionApi => {
// We can access global data through items
// If you get an error here about not able to access data
// on undefined, you need to check step 4
const events = collectionApi.items[0].data.events;
// We sort by date
events.sort((a, b) => {
return new Date(a.date) - new Date(b.date);
});
// Find the first event where its date
// is in the future
const nextEvent = events.find(event => {
const now = new Date();
const eventDate = new Date(event.date);
return now < eventDate;
});
// Create an API response object
let response;
if(!nextEvent) {
response = {
status: "NO_EVENT_SCHEDULED",
data: null,
};
} else {
response = {
status: "OK",
data: nextEvent
}
}
// Write the API endpoint function to
// our Netlify Function folder
fs.writeFileSync(
"netlify/functions/api/api.js",
`export default function(request, response) {
return Response.json(${JSON.stringify(response)})
}`
);
// Return list with our next event so
// this collection can be used in other
// parts of the website too.
return [nextEvent];
});
}
7 Build the project and test the API
If you now run npm run build
in your
project root, Eleventy should create a
_site
folder, create the
netlify/functions/api/api.js
and copy it
to _site
.
To get Netlify Functions to run on local development, we need to use Netlify CLI that will wrap up the Eleventy project inside a Netlify development server and serve all functions:
netlify dev
This will run Eleventy dev server (by default on port 8080) and a Netlify dev server (by default on port 8888) that routes the requests to Eleventy.
If you now point your browser to localhost:8888/.netlify/functions/api, you should see the API return an event object.
If you want to see what the response is when there are no events scheduled,
remove all future events from
_data/events.json
.
Things to note
As with any static site project, the data in the API is static. It won’t change to a newer event just because time passed. It will only ever update on build time.
In my community projects, that’s not really a problem because there’s always website updates the day following an event. Or I can trigger a manual build the next morning.
It’s good to keep in mind though if you plan to do this with a site that doesn’t get updates often. In case like that, you might want to set up some automated daily builds or change the Netlify Function to include the all the future events and do the logic for finding the next event there.
Whatever API endpoints you end up creating and whatever their responses look like, it’s important to document them well so that whoever consumes your API can rely on the documentation to get their code functioning properly in all cases and they don’t have to reverse engineer it.
Part 2: API design
When I started working on this, I wanted to think hard about the response to make sure it’s nice to use. I asked around in two places for opinions and advice: in Mastodon and in Koodiklinikka community.
I found these discussions very insightful and interesting. Here are some options and ideas that were presented.
HTTP 200 with status field
I ended up choosing an option where the response always has an HTTP code of
200 (Success) and it has a status
field
that says if an event exists or not.
My considerations for this was that I wanted the shape of the response to be
always the same. I also wanted to be explicit about such event not existing.
Sometimes it can be hard to know if
{}
or
[]
in response means that there are no
results or that something went wrong. I wanted to avoid that.
I thought about how this would be consumed by other code and checking for
status: "OK"
before parsing the event
data seemed like an okay approach.
HTTP 200 for next event, 404 if no such event
A strong candidate was to send 404 (Not found) if no future events had been scheduled. This way, the consumer of the API could check the status code and continue based on that.
In RESTful API design, endpoints are considered to point to resources and if there are no events, it means the requested event is not found and 404 is the status code to use.
I’m by no means of school of thought where every response is 200 and errors are included in the response (which is something that has become somewhat popular with the influence of GraphQL) but I could not quite convince myself that “No event scheduled” would be an error case in similar way as if a resource is missing in RESTful design.
In my thinking, asking for the next event is always valid and sometimes due to real life situations, one has not been scheduled yet but asking for it was still the right thing to do.
This 404 approach seemed to spark the most discussion, with people arguing both for and against it.
Provide something else for missing case
Couple of suggestions were around the idea of the response for missing event being represented in a different format / structure as the other case. I find these the hardest or most annoying ones to consume because you don’t only need to check for values (like status codes or status strings) but also for the type of the data.
Respond with empty object
Another potential option was to return an empty object ({}
). In that case, if there’s content inside that object, an event exists. I did
consider that one as well but as I mentioned earlier, I wanted to be more
explicit in my response that the case is that there are no events scheduled.
It’s a stronger message that the response is intended and not just
accidentally empty.
204 No content
An interesting and spicy idea brought up in both places was using 204 (No content). On a very surface level glance, it sounds like it could be useful but 204 is really meant for cases where an action was performed successfully but there’s nothing to return to the client and no expectation for the client to do anything. So it’s not really a suitable option here.
If something above resonated with you, let's start a discussion about it! Email me at juhamattisantala at gmail dot com and share your thoughts. In 2025, I want to have more deeper discussions with people from around the world and I'd love if you'd be part of that.