Ciudades API
The Ciudades (Cities) API provides read-only access to cities used throughout the POS system for geographic location management. Cities are essential for supplier registration, customer addresses, and location-based reporting.
Endpoints Overview
Method Endpoint Authentication Description GET /api/pos/ciudadRequired Get all cities
The Ciudades API is read-only. City management is typically handled through database administration tools or dedicated admin interfaces.
GET /api/pos/ciudad
Retrieve a complete list of all cities in the system, ordered alphabetically by city name.
Authentication
Bearer token received from the login endpoint Example: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response
Returns an array of city objects, each containing:
Unique city code identifier
Example Request
curl -X GET http://localhost:3000/api/pos/ciudad \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response Examples
Success Response (200 OK)
[
{
"ct_codigo" : "CT001" ,
"ct_descripcion" : "Ambato"
},
{
"ct_codigo" : "CT002" ,
"ct_descripcion" : "Cuenca"
},
{
"ct_codigo" : "CT003" ,
"ct_descripcion" : "Guayaquil"
},
{
"ct_codigo" : "CT004" ,
"ct_descripcion" : "Ibarra"
},
{
"ct_codigo" : "CT005" ,
"ct_descripcion" : "Loja"
},
{
"ct_codigo" : "CT006" ,
"ct_descripcion" : "Machala"
},
{
"ct_codigo" : "CT007" ,
"ct_descripcion" : "Manta"
},
{
"ct_codigo" : "CT008" ,
"ct_descripcion" : "Portoviejo"
},
{
"ct_codigo" : "CT009" ,
"ct_descripcion" : "Quito"
},
{
"ct_codigo" : "CT010" ,
"ct_descripcion" : "Riobamba"
},
{
"ct_codigo" : "CT011" ,
"ct_descripcion" : "Santo Domingo"
}
]
Results are ordered alphabetically by ct_descripcion for easy reference in dropdown menus and selection interfaces.
{
"message" : "Token inválido o expirado"
}
This error occurs when:
No Authorization header is provided
The token is invalid or malformed
The token has expired
{
"message" : "Error al listar ciudades"
}
Usage Examples
JavaScript - Populate City Dropdown
async function loadCities () {
try {
const response = await fetch ( 'http://localhost:3000/api/pos/ciudad' , {
headers: {
'Authorization' : `Bearer ${ token } `
}
});
const cities = await response . json ();
const select = document . getElementById ( 'citySelect' );
// Add default option
const defaultOption = document . createElement ( 'option' );
defaultOption . value = '' ;
defaultOption . textContent = 'Seleccione una ciudad' ;
select . appendChild ( defaultOption );
// Add city options
cities . forEach ( city => {
const option = document . createElement ( 'option' );
option . value = city . ct_codigo ;
option . textContent = city . ct_descripcion ;
select . appendChild ( option );
});
} catch ( error ) {
console . error ( 'Error loading cities:' , error );
}
}
Python - Create City Lookup
import requests
def get_cities_lookup ( token ):
"""Get cities as a dictionary for quick lookups"""
url = "http://localhost:3000/api/pos/ciudad"
headers = { "Authorization" : f "Bearer { token } " }
response = requests.get(url, headers = headers)
cities = response.json()
# Create a dictionary for quick lookups
cities_dict = {
city[ 'ct_codigo' ]: city[ 'ct_descripcion' ]
for city in cities
}
return cities_dict
# Usage
cities = get_cities_lookup(token)
print (cities[ 'CT009' ]) # Output: "Quito"
print (cities[ 'CT003' ]) # Output: "Guayaquil"
React - Cities Hook with Caching
import { useState , useEffect } from 'react' ;
function useCities ( token ) {
const [ cities , setCities ] = useState ([]);
const [ loading , setLoading ] = useState ( true );
const [ error , setError ] = useState ( null );
useEffect (() => {
async function fetchCities () {
// Check cache first
const cached = localStorage . getItem ( 'cities' );
const cacheTime = localStorage . getItem ( 'cities_cache_time' );
const now = Date . now ();
const CACHE_DURATION = 24 * 60 * 60 * 1000 ; // 24 hours
if ( cached && cacheTime && ( now - parseInt ( cacheTime )) < CACHE_DURATION ) {
setCities ( JSON . parse ( cached ));
setLoading ( false );
return ;
}
try {
const response = await fetch ( 'http://localhost:3000/api/pos/ciudad' , {
headers: {
'Authorization' : `Bearer ${ token } `
}
});
if ( ! response . ok ) {
throw new Error ( 'Failed to fetch cities' );
}
const data = await response . json ();
setCities ( data );
// Cache the results
localStorage . setItem ( 'cities' , JSON . stringify ( data ));
localStorage . setItem ( 'cities_cache_time' , now . toString ());
} catch ( err ) {
setError ( err . message );
} finally {
setLoading ( false );
}
}
fetchCities ();
}, [ token ]);
const getCityName = ( codigo ) => {
const city = cities . find ( c => c . ct_codigo === codigo );
return city ? city . ct_descripcion : codigo ;
};
return { cities , loading , error , getCityName };
}
// Usage in component
function SupplierForm () {
const token = localStorage . getItem ( 'token' );
const { cities , loading , error } = useCities ( token );
if ( loading ) return < div > Cargando ciudades... </ div > ;
if ( error ) return < div > Error: { error } </ div > ;
return (
< form >
< label htmlFor = "city" > Ciudad: </ label >
< select id = "city" name = "ct_codigo" required >
< option value = "" > Seleccione una ciudad </ option >
{ cities . map ( city => (
< option key = { city . ct_codigo } value = { city . ct_codigo } >
{ city . ct_descripcion }
</ option >
)) }
</ select >
</ form >
);
}
// Display component
function SupplierDetails ({ supplier }) {
const { getCityName } = useCities ( token );
return (
< div >
< h3 > { supplier . prv_razonsocial } </ h3 >
< p > Ciudad: { getCityName ( supplier . ct_codigo ) } </ p >
</ div >
);
}
TypeScript - Type-Safe Cities Service
interface Ciudad {
ct_codigo : string ;
ct_descripcion : string ;
}
class CitiesService {
private static cities : Ciudad [] = [];
private static lastFetch : number = 0 ;
private static CACHE_DURATION = 86400000 ; // 24 hours
static async getCities ( token : string ) : Promise < Ciudad []> {
const now = Date . now ();
// Return cached cities if still fresh
if ( this . cities . length > 0 && now - this . lastFetch < this . CACHE_DURATION ) {
return this . cities ;
}
const response = await fetch ( 'http://localhost:3000/api/pos/ciudad' , {
headers: {
'Authorization' : `Bearer ${ token } `
}
});
if ( ! response . ok ) {
throw new Error ( 'Failed to fetch cities' );
}
this . cities = await response . json ();
this . lastFetch = now ;
return this . cities ;
}
static findByCode ( codigo : string ) : Ciudad | undefined {
return this . cities . find ( c => c . ct_codigo === codigo );
}
static findByName ( name : string ) : Ciudad | undefined {
const normalizedName = name . toLowerCase (). trim ();
return this . cities . find ( c =>
c . ct_descripcion . toLowerCase () === normalizedName
);
}
static searchByName ( query : string ) : Ciudad [] {
const normalizedQuery = query . toLowerCase (). trim ();
return this . cities . filter ( c =>
c . ct_descripcion . toLowerCase (). includes ( normalizedQuery )
);
}
}
// Usage
const cities = await CitiesService . getCities ( token );
const quito = CitiesService . findByName ( 'Quito' );
const searchResults = CitiesService . searchByName ( 'gua' ); // ["Guayaquil"]
Vue.js - Cities Composable
import { ref , onMounted } from 'vue' ;
export function useCities ( token ) {
const cities = ref ([]);
const loading = ref ( true );
const error = ref ( null );
const fetchCities = async () => {
try {
const response = await fetch ( 'http://localhost:3000/api/pos/ciudad' , {
headers: {
'Authorization' : `Bearer ${ token } `
}
});
if ( ! response . ok ) {
throw new Error ( 'Failed to fetch cities' );
}
cities . value = await response . json ();
} catch ( err ) {
error . value = err . message ;
} finally {
loading . value = false ;
}
};
const getCityName = ( codigo ) => {
const city = cities . value . find ( c => c . ct_codigo === codigo );
return city ? city . ct_descripcion : codigo ;
};
onMounted ( fetchCities );
return {
cities ,
loading ,
error ,
getCityName
};
}
Error Codes
Status Code Description 200 Request successful 401 Unauthorized - invalid or missing token 500 Internal server error
Data Model
City Object Structure
interface Ciudad {
ct_codigo : string ; // Primary key, unique identifier
ct_descripcion : string ; // City name
}
Database Schema
The cities are stored in the ciudad table with the following structure:
Column Type Description ct_codigoVARCHAR(10) Primary key, city code ct_descripcionVARCHAR(100) City name
Unlike other master data tables (categories, units), the ciudad table does not include a status field (estado). All cities returned are considered active.
Common Use Cases
Supplier Management
Cities are primarily used in supplier registration and management:
Supplier Registration : Select supplier’s city location
Supplier Filtering : Filter suppliers by city
Geographic Reports : Generate supplier distribution reports
Logistics : Plan delivery routes based on supplier locations
Customer Management
Customer Addresses : Store customer location information
Delivery Zones : Define delivery areas by city
Sales Analysis : Analyze sales patterns by geographic region
Regional Pricing : Apply city-specific pricing if needed
Reporting and Analytics
Geographic Distribution : View business presence across cities
Regional Performance : Compare sales/purchases by city
Logistics Optimization : Plan routes and deliveries
Market Analysis : Identify expansion opportunities
Integration Points
The Ciudades API integrates with several POS modules:
Related Modules:
Proveedores : Suppliers are assigned cities through ct_codigo
Clientes : Customers may be assigned cities for location tracking
Reporting : Geographic reports use city data
Logistics : Delivery and route planning based on cities
Foreign Key References
Cities are referenced by:
proveedor.ct_codigo - Supplier location
cliente.ct_codigo - Customer location (if applicable)
Other location-based tables
Best Practices
Performance Optimization:
Cache cities data aggressively - it changes very rarely
Load once on application startup
Store in local storage or application state
Consider a cache TTL of 24-48 hours or even longer
Cities are perfect candidates for static data bundling
UI/UX Recommendations:
Provide autocomplete/search functionality for city selection
Sort alphabetically for easy browsing
Consider grouping by province/region for larger countries
Display city names in local language and script
Support keyboard navigation in dropdown menus
Data Integrity:
Validate ct_codigo before creating suppliers or customers
Handle missing city references gracefully in displays
Don’t hardcode city codes - always fetch from API
Maintain referential integrity - don’t delete cities with active references
Geographic Expansion
If your business expands to new cities:
Add City to Database
Insert new city into the ciudad table with unique ct_codigo and ct_descripcion
Clear Client Caches
Ensure client applications refresh their cached city data
Update Related Systems
Update delivery zones, pricing tables, and other location-based configurations
Test Integration
Verify that suppliers and customers can be created with the new city
Ecuador Cities Reference
The system typically includes major Ecuadorian cities:
Coastal Region (Costa)
Code City Province CT003 Guayaquil Guayas CT007 Manta Manabí CT008 Portoviejo Manabí CT006 Machala El Oro
Highland Region (Sierra)
Code City Province CT009 Quito Pichincha CT002 Cuenca Azuay CT001 Ambato Tungurahua CT010 Riobamba Chimborazo CT004 Ibarra Imbabura CT005 Loja Loja
Other Regions
Code City Province CT011 Santo Domingo Santo Domingo de los Tsáchilas
Your installation may include additional cities based on your business needs and geographic coverage.
Implementation Notes
Controller Implementation
The city controller is implemented in pos.ciudad.controller.js:
const CiudadController = {
async list ( req , res ) {
const pool = connectFromJWT ( req );
try {
const data = await CiudadModel . getAllQuery ( pool );
res . json ( data );
} catch ( error ) {
console . error ( 'ERROR CIUDADES:' , error );
res . status ( 500 ). json ({ message: 'Error al listar ciudades' });
} finally {
await pool . end ();
}
}
};
Model Implementation
The city model in ciudad.model.js executes a simple query:
const getAllQuery = async ( pool ) => {
const query = 'SELECT ct_codigo, ct_descripcion FROM ciudad ORDER BY ct_descripcion' ;
const result = await pool . query ( query );
return result . rows ;
};
Connection Management
Like all POS endpoints, cities use JWT-based connection pooling:
JWT token is verified by verifyToken middleware
Database credentials are extracted from the token payload
A dedicated connection pool is created with user credentials
Query is executed with proper permission scope
Connection pool is closed in the finally block
This ensures proper row-level security and audit trailing at the database level.
Extending the Cities System
For more complex geographic requirements, consider:
Hierarchical Locations
Add province/region hierarchy:
ALTER TABLE ciudad ADD COLUMN provincia VARCHAR ( 50 );
ALTER TABLE ciudad ADD COLUMN region VARCHAR ( 50 );
Coordinates
Add GPS coordinates for mapping:
ALTER TABLE ciudad ADD COLUMN latitud DECIMAL ( 10 , 8 );
ALTER TABLE ciudad ADD COLUMN longitud DECIMAL ( 11 , 8 );
Delivery Zones
Create delivery zone mappings:
CREATE TABLE zona_entrega (
zona_codigo VARCHAR ( 10 ) PRIMARY KEY ,
zona_descripcion VARCHAR ( 100 ),
ct_codigo VARCHAR ( 10 ) REFERENCES ciudad(ct_codigo)
);
API Response Optimization
For very large city lists, consider implementing:
Pagination : Add page/limit parameters
Search : Add query parameter for filtering
Partial Loading : Load cities on-demand as user types
Regionalization : Filter by province/region
Compression : Enable gzip compression for responses
Example enhanced endpoint:
GET /api/pos/ciudad?q=qui®ion=sierra&limit=20
These enhancements maintain backward compatibility while supporting advanced use cases.