A practical guide to how to add a lawyer job to your qbcore fivem server, with implementation decisions, validation steps, and security considerations for a production-minded project.
Introduction
Adding a lawyer job to a QBCore FiveM server requires more than UI and animations — it needs a clear server-side architecture, secure validation, persistent data, and robust testing. This guide walks through architecture decisions, QBCore-specific implementation patterns, supporting libraries such as ox_lib, cross-compatibility notes for ESX, and a short section on equivalent design patterns for Roblox (Luau) servers. Use the practical checklist and code examples to implement a lawyer job that is maintainable and secure.
High-level architecture
The lawyer job is composed of three major subsystems:
- Persistence and database: job definitions, employee lists, permissions, and any case or invoice records stored in SQL.
- Server authority and validation: server-side handlers to verify job state, money transactions, and permission checks. Client code must be considered untrusted.
- Client presentation and interaction: menus, markers, animations, and blips that call server functions via safe events and callbacks.
Keep the server authoritative — the server decides who is a lawyer, who can bill a client, and what transactions happen. Client code only requests actions and renders data the server confirms.
Data model and database considerations
Create a lightweight SQL schema that supports employees, law firm records, and invoices. Using QBCore’s export-compatible model and a migration script ensures changes are repeatable.
-- Example SQL (MySQL)
CREATE TABLE IF NOT EXISTS lawyer_firms (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(128) NOT NULL,
owner_identifier VARCHAR(64) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS lawyer_employees (
id INT AUTO_INCREMENT PRIMARY KEY,
firm_id INT NOT NULL,
identifier VARCHAR(64) NOT NULL,
role VARCHAR(32) DEFAULT 'associate',
FOREIGN KEY (firm_id) REFERENCES lawyer_firms(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS lawyer_invoices (
id INT AUTO_INCREMENT PRIMARY KEY,
firm_id INT NOT NULL,
client_identifier VARCHAR(64) NOT NULL,
amount INT NOT NULL,
paid TINYINT(1) DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Keep financial logic on the server and store identifiers using whichever identifier strategy your server uses (Steam, license, fivem:hex, or license2). Avoid storing passwords or sensitive PII.
QBCore implementation: file layout and responsibilities
Split the resource into clear responsibilities. Example resource layout:
- fxmanifest.lua — declares dependencies (qb-core, ox_lib optionally)
- server/main.lua — server-side event handlers, database queries, permission checks
- client/main.lua — UI, menus, interactions; no business logic
- config.lua — job names, blip settings, pay scale
- sql/ — migration files for schema
Use QBCore exports and server callbacks instead of trusting client-sent data. For example, when a client requests to create an invoice, the server must verify that the requester is an employee of the firm and that the client exists.
Server-side patterns
Implement server-side callbacks and events that validate every request. Example patterns:
- Use QBCore.Functions.GetPlayer to read and validate identifier and money
- Validate role/permission stored in the database before allowing an action
- Sanitize inputs used in SQL queries and use parameterized queries
- Emit events for logging/auditing rather than writing client logs
ox_lib, menus, and UI choices
ox_lib provides fast menu and UI primitives. If you use ox_lib:
- Keep menu construction client-side, but call server callbacks for all stateful actions
- Use ox_lib notifications for feedback only after server confirmation
- Limit sensitive actions (e.g., awarding money, firing someone) to server-only functions triggered by validated events
Example client flow: open menu → select "Issue Invoice" → client sends request with target identifier and amount → server verifies and returns success/failure → client displays confirmation.
Security and server validation
Security is essential. Adopt these concrete safeguards:
- Never trust client input: validate identifiers, amounts, and permissions on the server. Treat client messages as requests only.
- Rate-limit sensitive endpoints: prevent spamming invoice creation or payment requests via server-side counters or cooldowns.
- Audit actions: log changes to employee role, payouts, invoice deletions so admins can review suspicious behavior.
- Use parameterized SQL or prepared queries: prevent injection attacks. Do not build SQL by concatenating strings with client data.
- Permission model: use a role field and check both QBCore job and a database-backed permission list for elevated actions (e.g., managing a firm).
Code examples — safe server callback and client request
-- server/main.lua (QBCore)
QBCore.Functions.CreateCallback('lawyer:getEmployeeData', function(source, cb, identifier)
local src = source
local player = QBCore.Functions.GetPlayer(src)
-- Always validate server-side: only allow if requesting player has permission or is querying self
if not player then return cb(nil) end
-- Example check: only allow own data or firm managers
MySQL.query('SELECT * FROM lawyer_employees WHERE identifier = ?', { identifier }, function(result)
cb(result[1] or nil)
end)
end)
RegisterNetEvent('lawyer:createInvoice', function(clientIdentifier, amount)
local src = source
local player = QBCore.Functions.GetPlayer(src)
if not player or type(amount) ~= 'number' or amount <= 0 then return end
-- server verifies that src is an employee and belongs to the firm
-- perform parameterized insert
MySQL.insert('INSERT INTO lawyer_invoices (firm_id, client_identifier, amount) VALUES (?, ?, ?)', { firmId, clientIdentifier, amount })
-- notify and log
end)
Testing and debugging
Systematic testing ensures the job behaves in production:
- Unit test server functions where possible (mock QBCore exports and MySQL functions).
- Integration test common flows: hire/fire employee, create/pay invoice, edge cases like negative amounts and SQL failures.
- Simulate latency and dropped packets to verify the client gracefully retries when appropriate.
- Use verbose server logs for admin-only testing, then switch to structured logs for production.
Automated checks
Automate SQL migrations, resource validation, and basic integration tests in your CI pipeline. Include a pre-deploy step that verifies the fxmanifest dependency chain and ensures required exports are present.
Deployment and maintenance
Deploy with versioned resources and migrations:
- Run migration scripts in a transactional manner; keep backups before schema changes.
- Provide a feature-flag or disabled-by-default config for major changes to allow safe rollout.
- Monitor error rates and add alerts for repeated failed RPCs or SQL errors.
- Document public server events and callbacks to prevent breaking other resources that may integrate with the lawyer job.
If you need tooling for deployment or collaboration, consider managed app dashboards like the Stellar AI app to organize resources and logs: https://trystellarai.com/app
Roblox (Luau) equivalent considerations
When implementing an equivalent job on Roblox, keep strict server authority:
- Use RemoteEvents/RemoteFunctions where the server validates every request. The server is authoritative; clients only ask to perform actions.
- Never apply state changes based only on RemoteEvent parameters without validation on the server.
- Handle DataStore failures: use pcall with exponential backoff and local caching for temporary state when DataStore operations fail.
- Design for resilient player disconnects — persist critical state as soon as possible and recover gracefully.
Example: when a client asks to bill another player, the server checks the bill amount, verifies both players exist and are in the same session, and writes a DataStore record only after validation. If DataStore fails, inform the client and retry or fallback to a non-persistent state until resolved.
Checklist: deployment readiness
| Task | Status | Notes |
|---|---|---|
| Database migrations created and tested | ☐ | Transactional scripts and backups |
| Server validation covering all actions | ☐ | Includes permission checks and sanitization |
| Client only handles UI; no business logic | ☐ | Use server callbacks for every critical action |
| Error handling for MySQL/DataStore | ☐ | Retries and admin alerts configured |
| Automated tests for key flows | ☐ | Unit + integration checks |
| Logging and audit trails enabled | ☐ | Review logs for suspicious behavior |
Maintenance and future-proofing
Keep the job maintainable:
- Version your resource and communicate breaking changes to the admin community.
- Write migration scripts for every schema change and maintain a changelog.
- Abstract integrations with other resources (bank, phone, billing) via stable server exports rather than direct database access when possible.
If you run multiple servers or want an admin UI to manage resources, consider external tools and dashboards: https://trystellarai.com/app
Further reading and references
Read detailed implementation notes and related posts on the Stellar AI blog: https://trystellarai.com/blog