In this guide, we'll walk through the process of developing a RESTful API using ASP.NET Core, connecting it to a Neon Postgres database. We will cover CRUD operations using Entity Framework Core (EF Core), generate interactive API documentation with Swagger, and explore best practices for testing your API endpoints. As a bonus, we'll also implement JWT authentication to secure your endpoints.
Prerequisites
Before we start, make sure you have the following:
- .NET SDK 8.0
- Neon account for setting up your Postgres database
- Postman for API testing
- Basic knowledge of C# and ASP.NET Core
- Familiarity with Entity Framework Core
Setting Up Your ASP.NET Core Project
First, create a new ASP.NET Core Web API project:
Install the required NuGet packages using the dotnet add package
command:
The above packages include:
Microsoft.EntityFrameworkCore
- Entity Framework Core for database operationsNpgsql.EntityFrameworkCore.PostgreSQL
- PostgreSQL provider for EF CoreSwashbuckle.AspNetCore
- Swagger for API documentationMicrosoft.AspNetCore.Authentication.JwtBearer
- JWT authentication for securing endpointsMicrosoft.EntityFrameworkCore.Design
- EF Core design tools for migrations
Configuring the Neon Database
Head over to your Neon Dashboard and create a new project.
Once done, grab your database connection string and add it to your appsettings.json
:
While you're in appsettings.json
, add a section for JWT authentication after the connection string:
We will cover JWT authentication in more detail later in this guide, but for now, let's focus on setting up the API.
Next, update your Program.cs
file to include the database context, Swagger, and JWT authentication:
In the above, we configure the necessary services directly within Program.cs
to connect our ASP.NET Core API to Neon and secure it with JWT authentication:
-
We use
AddDbContext
to set upNeonDbContext
with the Npgsql provider, connecting to the Neon database using the connection string defined inappsettings.json
. Make sure to update"NeonDb"
with your actual connection string key if it's named differently. -
We register controllers with
AddControllers()
, which allows the application to handle incoming API requests and map them to their respective endpoints. -
By adding
EndpointsApiExplorer
andSwaggerGen
, we enable automatic generation of API documentation. This provides a user-friendly interface to interact with your API endpoints, accessible at/swagger
. -
For the JWT authentication setup, we are implementing the following:
- Here, we set up JWT authentication to protect your API routes. We use
AddJwtBearer
to validate tokens sent by clients. - The
TokenValidationParameters
section allows us to make sure that the token is signed with the specified key. Replace"your-secret-key"
inappsettings.json
with a secure key unique to your application. - For simplicity,
ValidateIssuer
andValidateAudience
are set tofalse
, which means the API won't check who issued the token or its intended audience. This is useful for local development but should be tightened for production environments.
- Here, we set up JWT authentication to protect your API routes. We use
To avoid hardcoding sensitive information like the secret key, consider using environment variables or a configuration management system to securely store secrets.
Creating the Entity Framework Core Models
Data models define the structure of your database tables and the relationships between them. Here, we'll create a simple Product
model to represent products in our Neon database.
In the Models
folder, create a Product.cs
file:
Here, we define a simple Product
model with four properties:
Id
: This is the primary key, which will auto-increment.Name
: Stores the product's name.Price
: Holds the product's price as a decimal value.Description
: Provides additional details about the product.
Each property corresponds to a column in the database table that Entity Framework will generate for us.
Creating the Database Context
Next, we need to create a database context class, which serves as a bridge between our C# code and the Neon database.
Create a new folder named Data
and add a NeonDbContext.cs
file:
The above code snippet does the following:
- The
NeonDbContext
class inherits fromDbContext
, which is part of Entity Framework Core. - We pass
DbContextOptions
to the constructor to configure the connection to our Neon database. - The
DbSet<Product>
property represents theProducts
table. This allows us to perform CRUD operations on theProduct
model directly through this context.
Running Migrations to Create the Database Schema
Now that we have defined our model and context, let's generate the database schema using migrations. Open your terminal and run the following commands:
The above command generates a migration file based on the changes made to the database schema. The migration file contains instructions to create the Products
table.
Next, apply the migration to your Neon database:
The dotnet ef database update
command applies the migration to your Neon database, creating the Products
table and any other necessary schema changes.
Note: Make sure your database connection string in
appsettings.json
is correctly configured before running the migrations. That way the changes are applied to your Neon database instance.
At this point, your database is set up and ready to store product data!
Building the API Endpoints
With the database schema in place, let's create the API endpoints to perform CRUD operations on the Products
table.
In the Controllers
folder, create a ProductsController.cs
file with the following content:
In the code above, we define a ProductsController
to handle all CRUD operations for our Product
model. Here's a breakdown of how each endpoint works:
-
The
GetProducts
method handlesGET /api/products
requests, fetching all products stored in the Neon database. -
The
GetProduct
method handlesGET /api/products/{id}
requests to retrieve a single product by its unique ID. If no product with the given ID is found, it responds with a404 Not Found
. This ensures the client is notified when attempting to access a non-existent product. -
The
CreateProduct
method handlesPOST /api/products
requests to add a new product. The response usesCreatedAtAction
to include a link to the newly created resource, following REST best practices. -
The
UpdateProduct
method handlesPUT /api/products/{id}
requests to modify an existing product. Once the update is successful, it responds with a204 No Content
, indicating the operation was successful without returning any additional data. -
The
DeleteProduct
method handlesDELETE /api/products/{id}
requests to remove a product by its ID. If the product doesn't exist, it returns a404 Not Found
response.
Each endpoint is fully asynchronous and interacts with the Neon database through the NeonDbContext
context.
Setting Up Swagger for API Documentation
To document our API and provide an interactive interface for testing, we'll integrate Swagger into our ASP.NET Core project.
Swagger automatically generates OpenAPI documentation, making it easy to explore your API and test its endpoints directly from your browser.
Program.cs
Enabling Swagger in To set up Swagger, add the following code in the Configure
method of your Program.cs
file:
The UseSwagger
middleware generates the OpenAPI documentation for your API, while UseSwaggerUI
sets up the Swagger interface for interacting with your endpoints.
Running Your Application
Now that Swagger is set up, start your application using:
Once the application is running, you will see the port where your API is hosted (usually https://localhost:5229
), eg.:
Visit https://localhost:5229/swagger
in your browser to access the Swagger UI.
The Swagger UI will appear, displaying all your API endpoints with detailed documentation. You can test the endpoints by sending requests directly from the UI and viewing the responses, making it easy to verify that everything works as expected.
Testing Your API with Postman
Now that your API is running, you can use Postman or any other API testing tool to interact with your endpoints. You can even use the Swagger UI to test the endpoints, but Postman provides a more robust environment for testing complex scenarios.
In this section, we'll walk through testing the CRUD operations using Postman but feel free to use any tool you're comfortable with like Insomnia or cURL.
Download and launch Postman. Create a new request to interact with your API.
Open Postman and create the following requests:
-
GET
:/api/products
- Description: Fetches all products.
- Set to
GET
, enterhttps://localhost:5001/api/products
, and click Send. - You should receive a
200 OK
response with a list of products.
-
POST
:/api/products
- Description: Creates a new product.
- Set to
POST
, enterhttps://localhost:5001/api/products
, and go to Body → raw → JSON. - Add:
- Click Send. Expect a
201 Created
response.
-
PUT
:/api/products/{id}
- Description: Updates a product.
- Set to
PUT
, enterhttps://localhost:5001/api/products/1
. - Add:
- Click Send. You should receive a
204 No Content
.
-
DELETE
:/api/products/{id}
- Description: Deletes a product.
- Set to
DELETE
, enterhttps://localhost:5001/api/products/1
, and click Send. - Expect a
204 No Content
.
After testing, check that all changes are reflected in your Neon database. Use both Postman and Swagger UI to confirm the endpoints are functioning correctly.
Securing Your API with JWT Authentication (Bonus)
To protect your API endpoints, we’ll use JWT (JSON Web Token) authentication. By adding the [Authorize]
attribute to specific controller actions, you can ensure that only authenticated users have access. Here’s how to secure the GetProducts
endpoint:
Now, any requests to GetProducts
will require a valid JWT token, if you try to access the endpoint without a token, you will receive a 401 Unauthorized
response.
Generating and Using JWT Tokens
When a user successfully logs in, the server generates a JWT token containing the user's authentication details. This token typically includes claims such as the user's ID, email, and a unique identifier. The token is then signed with a secret key to ensure its integrity and prevent tampering. For example:
The token looks like this:
If you were to go to jwt.io, you could paste the token and see its decoded contents.
Once the token is generated, the client stores it in local storage or session storage and includes it in the Authorization
header for all subsequent requests to secured endpoints. For instance:
With this header in place, the server can authenticate the user without requiring them to log in again for each request.
Conclusion
In this guide, we covered the process of building a RESTful API with ASP.NET Core, connecting it to a Neon Postgres database, and securing it with JWT authentication. We explored CRUD operations using Entity Framework Core, generated interactive API documentation with Swagger, and tested our endpoints using Postman.
As a next step, consider expanding your API with additional features, such as pagination, filtering, or sorting. You can also explore adding testing frameworks like xUnit or NUnit to write unit tests for your API endpoints.
For more information, check out:
Need help?
Join our Discord Server to ask questions or see what others are doing with Neon. Users on paid plans can open a support ticket from the console. For more details, see Getting Support.