Rails API
A starting template for a Rails API with login/sign-up functionality, PostgreSQL, RSpec and Pundit.
Repo
This template is a public repo on GitHub, and can be found here.
Specifications
This project template is setup with Ruby 3.3.0 and Rails 7.1.3.2, the Pundit authorization gem, and has a PostgreSQL database.
Testing has been setup with RSpec, including factory_bot, Faker and Database Cleaner Adapter for ActiveRecord.
It includes authentication setup with Rails' has_secure_password
(see 'Authentication' and 'Endpoints' sections below).
It also includes linting with a custom setup of Rubocop, and a GitHub workflow which run two checks, tests and linting, on each push or pull request to master
.
Setup
Feel free to clone this template or use it any way you see fit. However, the simplest way to get started is to:
-
Navigate to the template on GitHub.
-
Click 'Use this template'.
-
On the next screen, fill-in a repository name and click 'Create repository from template'.
-
On the next page, click the 'Code' button, and in the dropdown, copy the url beneath 'HTTPS'.
-
In your local terminal, CD into the folder where you want to store the project. Then type
git clone [THE URL YOU JUST COPIED]
, for examplegit clone https://github.com/jro31/my-new-project.git
, and pressEnter
. -
CD into the created repo, for example
cd my-new-project
. -
To check that all specs are passing, run
bundle exec rspec
. You should get 0 failures. -
Run
rails s
to start the server. Then navigate to localhost:3001. If all is well, you should see{"status":"It's working"}
.
Notes
Development
- Run the development server with
rails s
. - The default port is 3001.
- So when running, the api can be accessed at localhost:3001.
- Update the
origins "http://localhost:3000"
line ofconfig/initializers/cors.rb
with the development URL of your frontend. - Update both
key
values ofconfig/initializers/session_store.rb
with the name of your app. - Update all instances of
rails_api_template
inconfig/cable.yml
,config/database.yml
andconfig/environments/production.rb
to the name of your app, for example:config/cable/yml
- Update
channel_prefix: rails_api_template_production
tochannel_prefix: my_new_project_production
- Update
config/database.yml
- Update
database: rails_api_template_development
todatabase: my_new_project_development
- Update
#username: rails_api_template
to#username: my_new_project
- Update
database: rails_api_template_test
todatabase: my_new_project_test
- Update
database: rails_api_template_production
todatabase: my_new_project_production
- Update
username: rails_api_template
tousername: my_new_project
- Update
password: <%= ENV["RAILS_API_TEMPLATE_DATABASE_PASSWORD"] %>
topassword: <%= ENV["MY_NEW_PROJECT_DATABASE_PASSWORD"] %>
- Update
config/environments/production.rb
- Update
# config.active_job.queue_name_prefix = "rails_api_template_production"
to# config.active_job.queue_name_prefix = "my_new_project_production"
- Update
- This is necessary for nothing but brevity if you only ever use this template once. However, if you use it multiple times without updating these values (particularly those in
config/database.yml
), you will run into issues such as both apps will start using the same development database.
Production
- Update the
origins "https://myappurl.com"
line ofconfig/initializers/cors.rb
with the production URL of your frontend. - Update the
domain
value ofconfig/initializers/session_store.rb
with the production URL of your API.
Authentication
This template includes authentication setup using has_secure_password
. This includes:
-
A User model with email/password validations (and specs for these validations).
-
A registrations controller that includes a
#create
action:- This action allows a user to register (sign-up).
- If successful, it returns the status
:created
(201), with a json that includes the user ID, and the user email. - If unsuccessful, it returns the status
:unprocessable_entity
(422) with an error message. - This action has a request spec.
-
A sessions controller that has three actions:
-
The
#create
action allows a user to login.- If they provide a correct email/password combo, it returns the status
:created
(201), with a json that includes the user ID, and the user email. - Otherwise it returns the status
:unauthorized
(401) with an error message.
- If they provide a correct email/password combo, it returns the status
-
The
#logged_in
action utilises the CurrentUserConcern to check if a user is logged-in.- If they are it returns
logged_in: true
and the user, if not it returnslogged_in: false
.
- If they are it returns
-
The
#logout
action allows a user to logout. -
All actions have request specs.
-
Endpoints
GET
http://localhost:3001/
- Root, to check that the API is working.
POST
http://localhost:3001/api/v1/registrations
- To register a user.
- Requires a
user
param containing anemail
,password
andpassword_confirmation
, for example:
fetch('http://localhost:3001/api/v1/registrations', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
user: {
email: 'example@email.com',
password: 'password',
password_confirmation: 'password',
},
}),
credentials: 'include',
});
POST
http://localhost:3001/api/v1/sessions
- To create a session (login a user).
- Requires a
user
param containing anemail
andpassword
, for example:
fetch('http://localhost:3001/api/v1/sessions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
user: {
email: 'example@email.com',
password: 'password',
},
}),
credentials: 'include',
});
GET
http://localhost:3001/api/v1/logged_in
- To check if a user is logged-in.
- Example request:
fetch('http://localhost:3001/api/v1/logged_in', {
credentials: 'include',
});
DELETE
http://localhost:3001/api/v1/logout
- To logout a user.
- Example request:
fetch('http://localhost:3001/api/v1/logout', {
method: 'DELETE',
credentials: 'include',
});