Hello Developers!
Have you ever wanted to back-up datastore data? Have more complex data structures? Or maybe you want to be able to access your data from other platforms (eg. Discord), to do this you’ll need to use an external database.
When I wanted to do this myself, I didn’t even know where to begin and couldn’t find a helpful tutorial on here so I want to enlighten everyone who wants to create their own custom database!
Prerequisites:
- Roblox Studio Installed
- Replit Account
- MongoDB Account
Setting Up Our Database
The first thing we need to do is create the database on MongoDB. MongoDB has free and paid options, making it easy to scale your project as your game grows larger.
First things first, we need to make an account. Navigate to the top bar and look to the far right. Click Try Free.
Scroll down to the bottom, and sign up with your google account to make things fast and easy. Accept the terms of service and privacy policy. You’ll have to answer a quick quiz, fill it out based on the use of of your Roblox game. Make sure you set your preferred language to JavaScript as that’s what we’ll be using in this tutorial.
Once you click finish, you’ll be provided several options on how to pay for your database. In this case I will be choosing the free plan, but you can opt to pay for the database if you know you’ll need more space.
Once you get to the server setup page, this is where you can purchase more space or better performance. I am going to show the settings I opt for that provide a free and fast experience.
As for your cluster’s tier, I opt for the M0 cluster. This is free, and easy to scale. Although if your implementing this in an already profitable game I would recommend scaling to your needs.
Finally, choose a name!
After creating your cluster, you’ll be directed to the security tab. This will allow us to create logins so we can access the database later. Go ahead and make a root account by making the username “root” and the password anything you’d like. You can make multiple accounts if you plan to log the actions of specific administrators in your organization.
Make sure you remember your username and password, as this will be important later on.
Once you’ve added your login, we’ll be adding our IP addresses into the access list. Add the following IP adress (so that our Repl can access MongoDB): 0.0.0.0/0
At the bottom, click finish and close. Next click go to database.
Here we will click the connect button so we can connect to it through our Replit application.
You’ll want to click the “Connect your Application” button, and then make sure your settings are as follows.
Next, you’ll want to copy your connection SRV. Go ahead and copy it, it should look like this: mongodb+srv://root:password@tutorial-cluster.4hkyd.mongodb.net/?retryWrites=true&w=majority
Go ahead and find this part of the string root:password. You’ll want to replace password, and the <> with the password you made when creating your root account. After you’ve done this, go ahead and save that SRV somewhere as we’ll be using it to access the database on Replit.
Making the Replit Application
First we’ll navigate to the top bar and click sign-up.
Sign up with the same google account as earlier, to sign up quick and easy. Configure your display name, and bio as you wish and then finish setting up your account.
First thing you’ll want to do is navigate to the sidebar on the left side of your screen. At the top of that bar you’ll see a create button, go ahead and click that.
Choose the Node.js template, name the project, and create it! We’ll only need the default file to create the server as this will just be the messenger to our database.
Now we need to set up an environment variable. This is because the code will be public, so we need to hide the sensitive information from any nosy people that find your repl. So on the side bar, click the little lock icon and you should see this screen.
You need to title your variable (key field) “SRV”. For the value of it, paste in your SRV string we copied down earlier. Make sure you have put in the password correctly, or else we won’t be able to access the database from Replit.
Finally, click “Add new secret”.
Great! Now we can begin coding. This system works by having Roblox send a request to our Replit server, and then the Replit server will change the data in the database. To do this, we will need a couple different libraries. Express, and mongoose. Express will allow us to listen for the requests from Roblox and handle them, while mongoose will be the library connecting to MongoDB and changing the data. Replit automatically installs these libraries, so now we must require them! Start coding by requiring the libraries. Finally, we need body-parser. This is so that we can read the body of the requests made to our server.
const express = require("express")
const mongoose = require("mongoose")
const bodyParser = require("body-parser")
After you run the application, these will automatically be configured for you.
So now we need to start listening for requests by starting up a web server. Express makes this easy, just create a server variable using express’s built-in function. After we do that, we’ll start listening.
const app = express()
const listener = app.listen(3000, () => {
console.log("APP | Application listening on port " + listener.address().port);
});
In this code example, the application is listening on port 3000. If you run the app, it should now print to the console what port it’s listening on.
Now we need to tell our server what to do when it get’s a request. Right now we haven’t configured an endpoint so our application won’t be able to receive requests. We have to tell it specifically what URL’s to listen for, and what to do on each one. I am going to add one endpoint to find specific data, and an endpoint to save some data. Other endpoints will be taught in the sections below.
First things first, we need to tell our application to use the body-parser. We use this so that we can actually see the body (data) that is being sent in the request.
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
This code ensures that we will always be able to read the body of the requests sent to us. This is called middleware as it interacts with the requests in the middle of express processing it.
Now we will configure our two endpoints. Before we do, let’s go over the different types of requests you can have.
GET requests are the type of request you make when you want to get information from the server.
POST requests are the type of request you make when you want to add new information to the server.
PUT requests are the type of request you make when you want to edit information on the server.
DELETE requests are the type of request you make when you want to delete information off the server.
Now that we know the types of requests, it’s good to know that Roblox only supports GET and POST requests.
If you want to send data with your GET request (for example to find specific data); keep in mind Roblox will not let you send data with your GET requests so you should make a POST endpoint instead.
There are several functions you should use when creating an endpoint.
app.get()
app.post()
app.put()
app.delete()
We will start by creating a GET input to retrieve all of the data in our database.
app.get("/allData", async (request, response) => {
})
Before we start coding in the logic to retrieve and send back all of our data, let’s talk about what this function is doing. The first parameter is a string and shows the path off of the base URL that this code is listening for. For example if my base URL was “https://example.com/”, then this app would be listening for a GET request on “https://example.com/allData”.
Now we need to initiate a connection to mongoose. So you need to tell mongoose to connect and get ready to get the data.
mongoose.connect(process.env.SRV,{ useNewUrlParser: true, useUnifiedTopology: true });
var connection = mongoose.connection;
Our entire script should look like this so far.
const express = require("express")
const mongoose = require("mongoose")
const bodyParser = require("body-parser")
mongoose.connect(process.env.SRV,{ useNewUrlParser: true, useUnifiedTopology: true });
var connection = mongoose.connection;
const app = express()
const listener = app.listen(3000, () => {
console.log("APP | Application listening on port " + listener.address().port);
});
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.get("/allData", async (request, response) => {
})
Setting Up Models In Mongoose
So to code our first endpoint we will be simply retrieving all data in one of our models. Models are used to store specific types of data (Server model stores server information, player model stores player information etc.).
I am going to set-up a basic model for server information, you can have as many models as you’d like. Models are made based off of schemas, schemas will decide what data a model can have in it.
const serverSchema = new mongoose.Schema({
playerCount: Number,
serverName: String
})
const server = mongoose.model("Server", serverSchema)
Adding Endpoints
For the “allData” endpoint I want my application to retrieve all of the servers that I’ve saved information of in my database.
app.get("/allData", async (request, response) => {
let data
try {
-- Look in the database for all server data
data = await mongoose.model("Server").find({})
}
catch(err) {
-- Catch any errors and log them
console.log("DATABASE | Error: " + err)
}
if(data) {
-- Found the data, response with status 200 OK and send the data back
response.status(200)
response.json({
status: "success",
message: "200 | Servers found",
data: data
})
}
else {
-- Couldn't find any data, return a 404 not found status
response.status(404)
response.json({
status: "error",
message: "404 | Servers not found"
})
}
})
And now, I’ll add a endpoint to save some data as a server.
app.post("/addServer/", async (request, response) => {
-- Read the request body to find the new servers data
let playerCount = request.body.playerCount
let serverName = request.body.serverName
let data
try {
-- Check if another server already exsists
data = mongoose.model("Server").findOne({ serverName: `${serverName}` })
} catch (err) {
console.log("DATABASE | Error: " + err)
}
-- Save the new server's data
const serverModel = mongoose.model("Server");
const newServer = new serverModel({
playerCount: playerCount,
serverName: serverName
});
newServer.save()
-- Response with status 201 so they know it was created, send back the new server object
response.status(201)
response.json({
status: "success",
message: "201 | Server created",
data: newServer
})
})
Here’s our whole script.
const express = require("express")
const mongoose = require("mongoose")
const bodyParser = require("body-parser")
mongoose.connect(process.env.SRV,{ useNewUrlParser: true, useUnifiedTopology: true }).then(console.log("Connected!"))
var db = mongoose.connection;
const app = express()
const listener = app.listen(3000, () => {
console.log("APP | Application listening on port " + listener.address().port);
});
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
const serverSchema = new mongoose.Schema({
playerCount: Number,
serverName: String
})
const server = mongoose.model("Server", serverSchema)
app.get("/allData", async (request, response) => {
let data
try {
data = await mongoose.model("Server").find({})
}
catch(err) {
console.log("DATABASE | Error: " + err)
}
if(data) {
response.status(200)
response.json({
status: "success",
message: "200 | Servers found",
data: data
})
}
else {
response.status(404)
response.json({
status: "error",
message: "404 | Servers not found"
})
}
})
app.post("/addServer/", async (request, response) => {
let playerCount = request.body.playerCount
let serverName = request.body.serverName
const serverModel = mongoose.model("Server");
const newServer = new serverModel({
playerCount: playerCount,
serverName: serverName
});
newServer.save()
response.status(201)
response.json({
status: "success",
message: "201 | Server created",
data: newServer
})
})
Making a Request From Roblox
Now that we’ve set up everything to handle our data, let’s make a request from Roblox.
In order to make a request in Roblox, we will have to paste this into a server script.
local http = game:GetService("HttpService")
http:RequestAsync({
Url = "YourEndPointHere", -- Where to send the request
Method = "GET/POST", -- The Request Type
Headers = {
["Content-Type"] = "application/json" -- Tells server that we are sending JSON in the body
},
Body = {} -- Actual Body of the request (must be encoded with JSON)
})
You will have to change the parameters to fit your needs. I will make an example request that first creates two servers, and then gets all the servers and prints them. Note: In order to get the URL to send the request to, take the base URL from the picture below and add in the endpoint. Eg. https://Tutorial.walkerunknown.repl.co/allData.
local http = game:GetService("HttpService")
local function addServer(body)
local res = http:RequestAsync({
Url = "https://Tutorial.walkerunknown.repl.co/addServer", -- Where to send the request
Method = "POST", -- The Request Type
Headers = {
["Content-Type"] = "application/json" -- Tells server that we are sending JSON in the body
},
Body = http:JSONEncode(body) -- Actual Body of the request (must be encoded with JSON)
})
return res
end
local function getAllServers()
local res = http:RequestAsync({
Url = "https://Tutorial.walkerunknown.repl.co/allData", -- Where to send the request
Method = "GET", -- The Request Type
Headers = {
["Content-Type"] = "application/json" -- Tells server that we are sending JSON in the body
},
})
return res
end
local body = {
playerCount = 2,
serverName = "Server1"
}
addServer(body)
local body = {
playerCount = 1,
serverName = "Server2"
}
addServer(body)
local servers = getAllServers()
local decoded = http:JSONDecode(servers.Body)
for i,v in pairs(decoded.data) do
print("Found "..v.serverName)
end
Expected output:
Securing the Server
At the moment, anyone who wishes to add or get data from our database can. For security, we’re going to simply add an authentication header to our request on Roblox. Next, we will check the headers on the server and make sure there is an authentication header and it has the right value. This will act as a sort of password to allow our Roblox game and server to interact with each other but prevent others from using our database.
First, let’s modify the request code I used earlier to include and authorization header.
local http = game:GetService("HttpService")
http:RequestAsync({
Url = "YourEndPointHere", -- Where to send the request
Method = "GET/POST", -- The Request Type
Headers = {
["Content-Type"] = "application/json" -- Tells server that we are sending JSON in the body,
["authorization"] = "AuthKey" -- Gives the "password" to the server
},
Body = {} -- Actual Body of the request (must be encoded with JSON)
})
Now, you’ll basically be able to create your password. Personally, since this doesn’t need to be remembered, I just smash my keyboard and make a random 50 digit string. Websites can also produce this for you. Once you have your key, head back to your Repl and create a new variable at the top of the script.
const auth = "AuthKeyHere"
Now, just add an if statement to check if the auth header matches the auth key as soon as the request is made.
if(request.headers.authorization === auth) {
}
Congrats! You’ve created your own external database!
Here are all of the scripts in there entire, completed form.
Replit Script
const express = require("express")
const mongoose = require("mongoose")
const bodyParser = require("body-parser")
const auth = "AuthKeyHere"
mongoose.connect(process.env.SRV,{ useNewUrlParser: true, useUnifiedTopology: true }).then(console.log("Connected!"))
var db = mongoose.connection;
const app = express()
const listener = app.listen(3000, () => {
console.log("APP | Application listening on port " + listener.address().port);
});
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
const serverSchema = new mongoose.Schema({
playerCount: Number,
serverName: String
})
const server = mongoose.model("Server", serverSchema)
app.get("/allData", async (request, response) => {
if(request.headers.authorization === auth) {
let data
try {
data = await mongoose.model("Server").find({})
}
catch(err) {
console.log("DATABASE | Error: " + err)
}
if(data) {
response.status(200)
response.json({
status: "success",
message: "200 | Servers found",
data: data
})
}
else {
response.status(404)
response.json({
status: "error",
message: "404 | Servers not found"
})
}
}
})
app.post("/addServer/", async (request, response) => {
if(request.headers.authorization === auth) {
let playerCount = request.body.playerCount
let serverName = request.body.serverName
const serverModel = mongoose.model("Server");
const newServer = new serverModel({
playerCount: playerCount,
serverName: serverName
});
newServer.save()
response.status(201)
response.json({
status: "success",
message: "201 | Server created",
data: newServer
})
}
})
Roblox Script
local http = game:GetService("HttpService")
local function addServer(body)
local res = http:RequestAsync({
Url = "EndpointHere", -- Where to send the request
Method = "POST", -- The Request Type
Headers = {
["Content-Type"] = "application/json", -- Tells server that we are sending JSON in the body
["authorization"] = "AuthKeyHere"
},
Body = http:JSONEncode(body) -- Actual Body of the request (must be encoded with JSON)
})
return res
end
local function getAllServers()
local res = http:RequestAsync({
Url = "EndpointHere", -- Where to send the request
Method = "GET", -- The Request Type
Headers = {
["Content-Type"] = "application/json", -- Tells server that we are sending JSON in the body
["authorization"] = "AuthKeyHere"
},
})
return res
end
local body = {
playerCount = 2,
serverName = "Server1"
}
addServer(body)
local body = {
playerCount = 1,
serverName = "Server2"
}
addServer(body)
local servers = getAllServers()
local decoded = http:JSONDecode(servers.Body)
for i,v in pairs(decoded.data) do
print("Found "..v.serverName)
end
- Yes, thanks!
- I’m confused.
- Already knew this, thanks anyways!
0 voters
Last Update: 7/9/2022, Created: 7/9/2022